diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/conversion_report.json b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/conversion_report.json new file mode 100644 index 0000000000000000000000000000000000000000..76fe9fa1eb060089a5166b36f7c042f88be9c333 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/conversion_report.json @@ -0,0 +1,1775 @@ +[ + { + "tool_name": "anansescanpy", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/anansescanpy.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_anansescanpy/app/anansescanpy_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "aria2", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/aria2.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_aria2/app/aria2_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bcbio-nextgen", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bcbio-nextgen.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bcbio-nextgen/app/bcbio-nextgen_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bin2cell", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bin2cell.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bin2cell/app/bin2cell_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconda-repodata-patches", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconda-repodata-patches.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconda-repodata-patches/app/bioconda-repodata-patches_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-alabaster", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-alabaster.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-alabaster/app/bioconductor-alabaster_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-alabaster.sfe", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-alabaster.sfe.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-alabaster.sfe/app/bioconductor-alabaster.sfe_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-alabaster.spatial", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-alabaster.spatial.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-alabaster.spatial/app/bioconductor-alabaster.spatial_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-apl", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-apl.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-apl/app/bioconductor-apl_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-benchdamic", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-benchdamic.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-benchdamic/app/bioconductor-benchdamic_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-blase", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-blase.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-blase/app/bioconductor-blase_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-bulksignalr", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-bulksignalr.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-bulksignalr/app/bioconductor-bulksignalr_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-cardspa", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-cardspa.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-cardspa/app/bioconductor-cardspa_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-catscradle", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-catscradle.manual_bundle.txt", + "run_help_command": false, + "status": "failed", + "error": "refine failed (retry=1): Gemini request failed across all models. gemini-2.5-pro: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-3-flash-preview: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-2.5-flash: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n", + "output_file": "", + "dockerfile_ready": false + }, + { + "tool_name": "bioconductor-cdi", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-cdi.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-cdi/app/bioconductor-cdi_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-cellhashr", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-cellhashr.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-cellhashr/app/bioconductor-cellhashr_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-cellid", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-cellid.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-cellid/app/bioconductor-cellid_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-cellmigration", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-cellmigration.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-cellmigration/app/bioconductor-cellmigration_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-clusterfoldsimilarity", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-clusterfoldsimilarity.manual_bundle.txt", + "run_help_command": false, + "status": "failed", + "error": "refine failed (retry=1): Gemini request failed across all models. gemini-2.5-pro: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-3-flash-preview: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-2.5-flash: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n", + "output_file": "", + "dockerfile_ready": false + }, + { + "tool_name": "bioconductor-concordexr", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-concordexr.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-concordexr/app/bioconductor-concordexr_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-cotan", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-cotan.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-cotan/app/bioconductor-cotan_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-csoa", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-csoa.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-csoa/app/bioconductor-csoa_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-ctsv", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-ctsv.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-ctsv/app/bioconductor-ctsv_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-curatedatlasqueryr", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-curatedatlasqueryr.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-curatedatlasqueryr/app/bioconductor-curatedatlasqueryr_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-decontx", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-decontx.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-decontx/app/bioconductor-decontx_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-deconvobuddies", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-deconvobuddies.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-deconvobuddies/app/bioconductor-deconvobuddies_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-despace", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-despace.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-despace/app/bioconductor-despace_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-dino", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-dino.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-dino/app/bioconductor-dino_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-escher", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-escher.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-escher/app/bioconductor-escher_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-experimentsubset", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-experimentsubset.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-experimentsubset/app/bioconductor-experimentsubset_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-geomxtools", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-geomxtools.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-geomxtools/app/bioconductor-geomxtools_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-ggsc", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-ggsc.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-ggsc/app/bioconductor-ggsc_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-ggspavis", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-ggspavis.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-ggspavis/app/bioconductor-ggspavis_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-hcatonsildata", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-hcatonsildata.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-hcatonsildata/app/bioconductor-hcatonsildata_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-hoodscanr", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-hoodscanr.manual_bundle.txt", + "run_help_command": false, + "status": "failed", + "error": "autogenerate_mcp_tool failed: Gemini request failed across all models. gemini-2.5-pro: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-3-flash-preview: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-2.5-flash: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n", + "output_file": "", + "dockerfile_ready": false + }, + { + "tool_name": "bioconductor-imcdatasets", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-imcdatasets.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-imcdatasets/app/bioconductor-imcdatasets_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-imcrtools", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-imcrtools.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-imcrtools/app/bioconductor-imcrtools_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-irisfgm", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-irisfgm.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-irisfgm/app/bioconductor-irisfgm_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-lisaclust", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-lisaclust.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-lisaclust/app/bioconductor-lisaclust_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-mastr", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-mastr.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-mastr/app/bioconductor-mastr_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-merfishdata", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-merfishdata.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-merfishdata/app/bioconductor-merfishdata_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-moleculeexperiment", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-moleculeexperiment.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-moleculeexperiment/app/bioconductor-moleculeexperiment_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-mousegastrulationdata", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-mousegastrulationdata.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-mousegastrulationdata/app/bioconductor-mousegastrulationdata_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-nnsvg", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-nnsvg.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-nnsvg/app/bioconductor-nnsvg_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-partcnv", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-partcnv.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-partcnv/app/bioconductor-partcnv_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-pipecomp", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-pipecomp.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-pipecomp/app/bioconductor-pipecomp_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-reactomegsa.data", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-reactomegsa.data.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-reactomegsa.data/app/bioconductor-reactomegsa.data_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-regionalst", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-regionalst.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-regionalst/app/bioconductor-regionalst_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-scalign", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-scalign.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-scalign/app/bioconductor-scalign_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-scannotatr", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-scannotatr.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-scannotatr/app/bioconductor-scannotatr_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-scarray.sat", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-scarray.sat.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-scarray.sat/app/bioconductor-scarray.sat_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-scbubbletree", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-scbubbletree.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-scbubbletree/app/bioconductor-scbubbletree_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-scclassifr", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-scclassifr.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-scclassifr/app/bioconductor-scclassifr_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-scdotplot", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-scdotplot.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-scdotplot/app/bioconductor-scdotplot_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-scfeatures", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-scfeatures.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-scfeatures/app/bioconductor-scfeatures_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-scider", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-scider.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-scider/app/bioconductor-scider_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-scqtltools", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-scqtltools.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-scqtltools/app/bioconductor-scqtltools_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-scrnaseqapp", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-scrnaseqapp.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-scrnaseqapp/app/bioconductor-scrnaseqapp_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-sctreeviz", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-sctreeviz.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-sctreeviz/app/bioconductor-sctreeviz_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-scvir", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-scvir.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-scvir/app/bioconductor-scvir_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-seraster", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-seraster.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-seraster/app/bioconductor-seraster_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-signifinder", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-signifinder.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-signifinder/app/bioconductor-signifinder_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-singlecelltk", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-singlecelltk.manual_bundle.txt", + "run_help_command": false, + "status": "failed", + "error": "autogenerate_mcp_tool failed: Gemini request failed across all models. gemini-2.5-pro: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-3-flash-preview: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-2.5-flash: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n", + "output_file": "", + "dockerfile_ready": false + }, + { + "tool_name": "bioconductor-smoppix", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-smoppix.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-smoppix/app/bioconductor-smoppix_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-sosta", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-sosta.manual_bundle.txt", + "run_help_command": false, + "status": "failed", + "error": "refine failed (retry=1): Gemini request failed across all models. gemini-2.5-pro: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-3-flash-preview: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-2.5-flash: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n", + "output_file": "", + "dockerfile_ready": false + }, + { + "tool_name": "bioconductor-spacetrooper", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-spacetrooper.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-spacetrooper/app/bioconductor-spacetrooper_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-spanorm", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-spanorm.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-spanorm/app/bioconductor-spanorm_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-spari", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-spari.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-spari/app/bioconductor-spari_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-spasim", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-spasim.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-spasim/app/bioconductor-spasim_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-spatialdatasets", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-spatialdatasets.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-spatialdatasets/app/bioconductor-spatialdatasets_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-spatialde", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-spatialde.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-spatialde/app/bioconductor-spatialde_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-spatialdmelxsim", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-spatialdmelxsim.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-spatialdmelxsim/app/bioconductor-spatialdmelxsim_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-spatialfeatureexperiment", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-spatialfeatureexperiment.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-spatialfeatureexperiment/app/bioconductor-spatialfeatureexperiment_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-spatiallibd", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-spatiallibd.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-spatiallibd/app/bioconductor-spatiallibd_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-spatialomicsoverlay", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-spatialomicsoverlay.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-spatialomicsoverlay/app/bioconductor-spatialomicsoverlay_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-spatialsimgp", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-spatialsimgp.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-spatialsimgp/app/bioconductor-spatialsimgp_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-speckle", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-speckle.manual_bundle.txt", + "run_help_command": false, + "status": "failed", + "error": "refine failed (retry=1): Gemini request failed across all models. gemini-2.5-pro: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-3-flash-preview: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-2.5-flash: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n", + "output_file": "", + "dockerfile_ready": false + }, + { + "tool_name": "bioconductor-spiat", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-spiat.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-spiat/app/bioconductor-spiat_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-spicyr", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-spicyr.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-spicyr/app/bioconductor-spicyr_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-spoon", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-spoon.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-spoon/app/bioconductor-spoon_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-spotclean", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-spotclean.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-spotclean/app/bioconductor-spotclean_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-spotsweeper", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-spotsweeper.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-spotsweeper/app/bioconductor-spotsweeper_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-standr", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-standr.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-standr/app/bioconductor-standr_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-statial", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-statial.manual_bundle.txt", + "run_help_command": false, + "status": "failed", + "error": "refine failed (retry=2): Gemini request failed across all models. gemini-2.5-pro: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-3-flash-preview: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-2.5-flash: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n", + "output_file": "", + "dockerfile_ready": false + }, + { + "tool_name": "bioconductor-stexampledata", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-stexampledata.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-stexampledata/app/bioconductor-stexampledata_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-stjoincount", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-stjoincount.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-stjoincount/app/bioconductor-stjoincount_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-stpipe", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-stpipe.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-stpipe/app/bioconductor-stpipe_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-subcellularspatialdata", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-subcellularspatialdata.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-subcellularspatialdata/app/bioconductor-subcellularspatialdata_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-svp", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-svp.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-svp/app/bioconductor-svp_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-synergyfinder", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-synergyfinder.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-synergyfinder/app/bioconductor-synergyfinder_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-tenxvisiumdata", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-tenxvisiumdata.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-tenxvisiumdata/app/bioconductor-tenxvisiumdata_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-tidyomics", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-tidyomics.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-tidyomics/app/bioconductor-tidyomics_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-tidyspatialexperiment", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-tidyspatialexperiment.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-tidyspatialexperiment/app/bioconductor-tidyspatialexperiment_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-tpsvg", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-tpsvg.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-tpsvg/app/bioconductor-tpsvg_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-vectrapolarisdata", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-vectrapolarisdata.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-vectrapolarisdata/app/bioconductor-vectrapolarisdata_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-visiumio", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-visiumio.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-visiumio/app/bioconductor-visiumio_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-voyager", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-voyager.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-voyager/app/bioconductor-voyager_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-weberdivechalcdata", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-weberdivechalcdata.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-weberdivechalcdata/app/bioconductor-weberdivechalcdata_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-xeniumio", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-xeniumio.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-xeniumio/app/bioconductor-xeniumio_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-xenlite", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-xenlite.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-xenlite/app/bioconductor-xenlite_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bwa", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bwa.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bwa/app/bwa_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "cascade-reg", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/cascade-reg.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_cascade-reg/app/cascade-reg_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "cell2cell", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/cell2cell.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_cell2cell/app/cell2cell_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "cellitac", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/cellitac.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_cellitac/app/cellitac_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "cellqc", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/cellqc.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_cellqc/app/cellqc_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "cellsnake", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/cellsnake.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_cellsnake/app/cellsnake_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "celltypist-so", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/celltypist-so.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_celltypist-so/app/celltypist-so_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "checkatlas", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/checkatlas.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_checkatlas/app/checkatlas_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "comebin", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/comebin.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_comebin/app/comebin_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "cosg", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/cosg.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_cosg/app/cosg_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "cospar", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/cospar.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_cospar/app/cospar_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "dca", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/dca.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_dca/app/dca_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "doubletdetection", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/doubletdetection.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_doubletdetection/app/doubletdetection_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "dropkick", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/dropkick.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_dropkick/app/dropkick_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "easy_vitessce", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/easy_vitessce.manual_bundle.txt", + "run_help_command": false, + "status": "failed", + "error": "refine failed (retry=1): Gemini request failed across all models. gemini-2.5-pro: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-3-flash-preview: exception HTTPSConnectionPool(host='generativelanguage.googleapis.com', port=443): Read timed out. (read timeout=180) | gemini-2.5-flash: exception HTTPSConnectionPool(host='generativelanguage.googleapis.com', port=443): Read timed out. (read timeout=180)", + "output_file": "", + "dockerfile_ready": false + }, + { + "tool_name": "echidna", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/echidna.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_echidna/app/echidna_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "episcanpy", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/episcanpy.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_episcanpy/app/episcanpy_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "flagx", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/flagx.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_flagx/app/flagx_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "gene-trajectory-python", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/gene-trajectory-python.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_gene-trajectory-python/app/gene-trajectory-python_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "genecircuitry", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/genecircuitry.manual_bundle.txt", + "run_help_command": false, + "status": "failed", + "error": "refine failed (retry=1): Gemini request failed across all models. gemini-2.5-pro: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-3-flash-preview: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-2.5-flash: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n", + "output_file": "", + "dockerfile_ready": false + }, + { + "tool_name": "htslib", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/htslib.manual_bundle.txt", + "run_help_command": false, + "status": "failed", + "error": "refine failed (retry=1): Gemini request failed across all models. gemini-2.5-pro: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-3-flash-preview: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-2.5-flash: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n", + "output_file": "", + "dockerfile_ready": false + }, + { + "tool_name": "kb-python", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/kb-python.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_kb-python/app/kb-python_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "khipu-metabolomics", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/khipu-metabolomics.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_khipu-metabolomics/app/khipu-metabolomics_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "last", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/last.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_last/app/last_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "liana", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/liana.manual_bundle.txt", + "run_help_command": false, + "status": "failed", + "error": "autogenerate_mcp_tool failed: Gemini request failed across all models. gemini-2.5-pro: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-3-flash-preview: exception HTTPSConnectionPool(host='generativelanguage.googleapis.com', port=443): Read timed out. (read timeout=180) | gemini-2.5-flash: exception HTTPSConnectionPool(host='generativelanguage.googleapis.com', port=443): Read timed out. (read timeout=180)", + "output_file": "", + "dockerfile_ready": false + }, + { + "tool_name": "mameshiba", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/mameshiba.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_mameshiba/app/mameshiba_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "markerrepo", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/markerrepo.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_markerrepo/app/markerrepo_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "mobivision-m", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/mobivision-m.manual_bundle.txt", + "run_help_command": false, + "status": "failed", + "error": "autogenerate_mcp_tool failed: Gemini request failed across all models. gemini-2.5-pro: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-3-flash-preview: exception HTTPSConnectionPool(host='generativelanguage.googleapis.com', port=443): Read timed out. (read timeout=180) | gemini-2.5-flash: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n", + "output_file": "", + "dockerfile_ready": false + }, + { + "tool_name": "msproteomicstools", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/msproteomicstools.manual_bundle.txt", + "run_help_command": false, + "status": "failed", + "error": "autogenerate_mcp_tool failed: Gemini request failed across all models. gemini-2.5-pro: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-3-flash-preview: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-2.5-flash: exception HTTPSConnectionPool(host='generativelanguage.googleapis.com', port=443): Read timed out. (read timeout=180)", + "output_file": "", + "dockerfile_ready": false + }, + { + "tool_name": "multiqc-xenium-extra", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/multiqc-xenium-extra.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_multiqc-xenium-extra/app/multiqc-xenium-extra_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "ngs-smap", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/ngs-smap.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_ngs-smap/app/ngs-smap_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "novae", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/novae.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_novae/app/novae_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "openms-thirdparty", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/openms-thirdparty.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_openms-thirdparty/app/openms-thirdparty_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "opticlust", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/opticlust.manual_bundle.txt", + "run_help_command": false, + "status": "failed", + "error": "refine failed (retry=1): Gemini request failed across all models. gemini-2.5-pro: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-3-flash-preview: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-2.5-flash: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n", + "output_file": "", + "dockerfile_ready": false + }, + { + "tool_name": "ourotools", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/ourotools.manual_bundle.txt", + "run_help_command": false, + "status": "failed", + "error": "refine failed (retry=1): Gemini request failed across all models. gemini-2.5-pro: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-3-flash-preview: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-2.5-flash: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n", + "output_file": "", + "dockerfile_ready": false + }, + { + "tool_name": "palantir", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/palantir.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_palantir/app/palantir_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "paste-bio", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/paste-bio.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_paste-bio/app/paste-bio_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "pcdl", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/pcdl.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_pcdl/app/pcdl_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "peakqc", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/peakqc.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_peakqc/app/peakqc_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "peptides", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/peptides.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_peptides/app/peptides_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "piaso", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/piaso.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_piaso/app/piaso_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "pixelator", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/pixelator.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_pixelator/app/pixelator_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "proteomiqon-peptidedb", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/proteomiqon-peptidedb.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_proteomiqon-peptidedb/app/proteomiqon-peptidedb_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "pyrovelocity", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/pyrovelocity.manual_bundle.txt", + "run_help_command": false, + "status": "failed", + "error": "autogenerate_mcp_tool failed: Gemini request failed across all models. gemini-2.5-pro: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-3-flash-preview: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-2.5-flash: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n", + "output_file": "", + "dockerfile_ready": false + }, + { + "tool_name": "qcatch", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/qcatch.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_qcatch/app/qcatch_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "r-archr", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/r-archr.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_r-archr/app/r-archr_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "r-azimuth", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/r-azimuth.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_r-azimuth/app/r-azimuth_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "r-beyondcell", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/r-beyondcell.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_r-beyondcell/app/r-beyondcell_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "r-cdseq", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/r-cdseq.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_r-cdseq/app/r-cdseq_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "r-dwls", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/r-dwls.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_r-dwls/app/r-dwls_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "r-epitrace", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/r-epitrace.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_r-epitrace/app/r-epitrace_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "r-flanders", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/r-flanders.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_r-flanders/app/r-flanders_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "r-mams", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/r-mams.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_r-mams/app/r-mams_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "r-redeemr", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/r-redeemr.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_r-redeemr/app/r-redeemr_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "r-sceasy", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/r-sceasy.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_r-sceasy/app/r-sceasy_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "r-scopfunctions", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/r-scopfunctions.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_r-scopfunctions/app/r-scopfunctions_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "r-seurat-data", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/r-seurat-data.manual_bundle.txt", + "run_help_command": false, + "status": "failed", + "error": "autogenerate_mcp_tool failed: Gemini request failed across all models. gemini-2.5-pro: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-3-flash-preview: exception HTTPSConnectionPool(host='generativelanguage.googleapis.com', port=443): Read timed out. (read timeout=180) | gemini-2.5-flash: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n", + "output_file": "", + "dockerfile_ready": false + }, + { + "tool_name": "r-seurat-disk", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/r-seurat-disk.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_r-seurat-disk/app/r-seurat-disk_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "sage-proteomics", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/sage-proteomics.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_sage-proteomics/app/sage-proteomics_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "samap", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/samap.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_samap/app/samap_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "samtools", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/samtools.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_samtools/app/samtools_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "sc-musketeers", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/sc-musketeers.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_sc-musketeers/app/sc-musketeers_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "scaden", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/scaden.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_scaden/app/scaden_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "scanpy-cli", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/scanpy-cli.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_scanpy-cli/app/scanpy-cli_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "scanpy-scripts", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/scanpy-scripts.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_scanpy-scripts/app/scanpy-scripts_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "sccellfie", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/sccellfie.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_sccellfie/app/sccellfie_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "scepia", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/scepia.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_scepia/app/scepia_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "scglue", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/scglue.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_scglue/app/scglue_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "scib", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/scib.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_scib/app/scib_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "scirpy", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/scirpy.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_scirpy/app/scirpy_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "scmidas", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/scmidas.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_scmidas/app/scmidas_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "scrnasim-toolz", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/scrnasim-toolz.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_scrnasim-toolz/app/scrnasim-toolz_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "scspectra", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/scspectra.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_scspectra/app/scspectra_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "scstem", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/scstem.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_scstem/app/scstem_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "sctriangulate", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/sctriangulate.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_sctriangulate/app/sctriangulate_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "scvi", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/scvi.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_scvi/app/scvi_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "scvi-tools", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/scvi-tools.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_scvi-tools/app/scvi-tools_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "scvis", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/scvis.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_scvis/app/scvis_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "scvis_galaxy", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/scvis_galaxy.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_scvis_galaxy/app/scvis_galaxy_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "scxmatch", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/scxmatch.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_scxmatch/app/scxmatch_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "sdeper", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/sdeper.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_sdeper/app/sdeper_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "seacells", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/seacells.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_seacells/app/seacells_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "shiba", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/shiba.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_shiba/app/shiba_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "simo-omics", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/simo-omics.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_simo-omics/app/simo-omics_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "sincei", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/sincei.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_sincei/app/sincei_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "sopa", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/sopa.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_sopa/app/sopa_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "spagrn", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/spagrn.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_spagrn/app/spagrn_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "spapros", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/spapros.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_spapros/app/spapros_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "spatialleiden", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/spatialleiden.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_spatialleiden/app/spatialleiden_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "switchtfi", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/switchtfi.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_switchtfi/app/switchtfi_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "talon", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/talon.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_talon/app/talon_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "tirank", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/tirank.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_tirank/app/tirank_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "vitessce-python", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/vitessce-python.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_vitessce-python/app/vitessce-python_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "vpt", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/vpt.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_vpt/app/vpt_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "wasp2", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/wasp2.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_wasp2/app/wasp2_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "xclone", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/xclone.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_xclone/app/xclone_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "zol", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/zol.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_zol/app/zol_server.py", + "dockerfile_ready": true + } +] \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/conversion_report.shard_0_of_4.json b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/conversion_report.shard_0_of_4.json new file mode 100644 index 0000000000000000000000000000000000000000..f782ff1ae07203acee0c89d84013b0e70e800ea6 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/conversion_report.shard_0_of_4.json @@ -0,0 +1,2108 @@ +[ + { + "tool_name": "abnumber", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/abnumber.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_abnumber/app/abnumber_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "alfred", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/alfred.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_alfred/app/alfred_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "aria2", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/aria2.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_aria2/app/aria2_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "arvados-cwl-runner", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/arvados-cwl-runner.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_arvados-cwl-runner/app/arvados-cwl-runner_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "auspice", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/auspice.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_auspice/app/auspice_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bcbio-nextgen", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bcbio-nextgen.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bcbio-nextgen/app/bcbio-nextgen_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "biobambam", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/biobambam.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_biobambam/app/biobambam_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-affy", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-affy.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-affy/app/bioconductor-affy_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-alabaster.spatial", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-alabaster.spatial.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-alabaster.spatial/app/bioconductor-alabaster.spatial_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-apl", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-apl.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-apl/app/bioconductor-apl_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-biocbaseutils", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-biocbaseutils.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-biocbaseutils/app/bioconductor-biocbaseutils_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-biovizbase", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-biovizbase.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-biovizbase/app/bioconductor-biovizbase_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-bulksignalr", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-bulksignalr.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-bulksignalr/app/bioconductor-bulksignalr_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-cellhashr", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-cellhashr.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-cellhashr/app/bioconductor-cellhashr_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-clusterprofiler", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-clusterprofiler.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-clusterprofiler/app/bioconductor-clusterprofiler_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-cotan", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-cotan.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-cotan/app/bioconductor-cotan_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-dada2", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-dada2.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-dada2/app/bioconductor-dada2_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-decontx", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-decontx.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-decontx/app/bioconductor-decontx_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-deseq2", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-deseq2.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-deseq2/app/bioconductor-deseq2_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-dirichletmultinomial", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-dirichletmultinomial.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-dirichletmultinomial/app/bioconductor-dirichletmultinomial_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-edger", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-edger.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-edger/app/bioconductor-edger_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-experimentsubset", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-experimentsubset.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-experimentsubset/app/bioconductor-experimentsubset_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-genomeinfodb", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-genomeinfodb.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-genomeinfodb/app/bioconductor-genomeinfodb_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-genomicranges", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-genomicranges.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-genomicranges/app/bioconductor-genomicranges_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-ggtree", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-ggtree.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-ggtree/app/bioconductor-ggtree_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-graph", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-graph.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-graph/app/bioconductor-graph_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-humanhippocampus2024", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-humanhippocampus2024.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-humanhippocampus2024/app/bioconductor-humanhippocampus2024_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-interactivedisplaybase", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-interactivedisplaybase.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-interactivedisplaybase/app/bioconductor-interactivedisplaybase_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-keggrest", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-keggrest.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-keggrest/app/bioconductor-keggrest_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-matrixgenerics", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-matrixgenerics.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-matrixgenerics/app/bioconductor-matrixgenerics_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-moleculeexperiment", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-moleculeexperiment.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-moleculeexperiment/app/bioconductor-moleculeexperiment_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-multtest", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-multtest.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-multtest/app/bioconductor-multtest_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-orfhunter", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-orfhunter.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-orfhunter/app/bioconductor-orfhunter_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-partcnv", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-partcnv.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-partcnv/app/bioconductor-partcnv_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-preprocesscore", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-preprocesscore.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-preprocesscore/app/bioconductor-preprocesscore_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-reactomegsa.data", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-reactomegsa.data.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-reactomegsa.data/app/bioconductor-reactomegsa.data_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-rhdf5filters", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-rhdf5filters.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-rhdf5filters/app/bioconductor-rhdf5filters_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-rsubread", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-rsubread.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-rsubread/app/bioconductor-rsubread_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-scaledmatrix", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-scaledmatrix.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-scaledmatrix/app/bioconductor-scaledmatrix_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-scater", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-scater.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-scater/app/bioconductor-scater_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-scfeatures", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-scfeatures.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-scfeatures/app/bioconductor-scfeatures_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-scrnaseqapp", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-scrnaseqapp.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-scrnaseqapp/app/bioconductor-scrnaseqapp_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-seraster", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-seraster.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-seraster/app/bioconductor-seraster_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-singlecelltk", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-singlecelltk.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-singlecelltk/app/bioconductor-singlecelltk_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-spacetrooper", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-spacetrooper.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-spacetrooper/app/bioconductor-spacetrooper_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-sparsearray", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-sparsearray.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-sparsearray/app/bioconductor-sparsearray_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-spatialde", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-spatialde.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-spatialde/app/bioconductor-spatialde_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-spatialfeatureexperiment", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-spatialfeatureexperiment.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-spatialfeatureexperiment/app/bioconductor-spatialfeatureexperiment_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-speckle", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-speckle.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-speckle/app/bioconductor-speckle_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-spotclean", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-spotclean.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-spotclean/app/bioconductor-spotclean_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-stexampledata", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-stexampledata.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-stexampledata/app/bioconductor-stexampledata_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-summarizedexperiment", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-summarizedexperiment.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-summarizedexperiment/app/bioconductor-summarizedexperiment_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-tenxxeniumdata", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-tenxxeniumdata.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-tenxxeniumdata/app/bioconductor-tenxxeniumdata_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-treeio", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-treeio.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-treeio/app/bioconductor-treeio_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-vectrapolarisdata", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-vectrapolarisdata.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-vectrapolarisdata/app/bioconductor-vectrapolarisdata_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-weberdivechalcdata", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-weberdivechalcdata.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-weberdivechalcdata/app/bioconductor-weberdivechalcdata_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-xvector", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-xvector.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-xvector/app/bioconductor-xvector_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "blast", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/blast.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_blast/app/blast_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bpipe", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bpipe.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bpipe/app/bpipe_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "c-ares", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/c-ares.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_c-ares/app/c-ares_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "cell2cell", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/cell2cell.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_cell2cell/app/cell2cell_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "celltypist-so", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/celltypist-so.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_celltypist-so/app/celltypist-so_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "circos", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/circos.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_circos/app/circos_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "cnvkit", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/cnvkit.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_cnvkit/app/cnvkit_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "cooler", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/cooler.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_cooler/app/cooler_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "cospar", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/cospar.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_cospar/app/cospar_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "cromwell", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/cromwell.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_cromwell/app/cromwell_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "cytoscape", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/cytoscape.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_cytoscape/app/cytoscape_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "deblur", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/deblur.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_deblur/app/deblur_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "dendropy", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/dendropy.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_dendropy/app/dendropy_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "dropkick", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/dropkick.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_dropkick/app/dropkick_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "echidna", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/echidna.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_echidna/app/echidna_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "entrez-direct", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/entrez-direct.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_entrez-direct/app/entrez-direct_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "f5c", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/f5c.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_f5c/app/f5c_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "fastdtw", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/fastdtw.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_fastdtw/app/fastdtw_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "fermi2", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/fermi2.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_fermi2/app/fermi2_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "flye", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/flye.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_flye/app/flye_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "fwdpy11", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/fwdpy11.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_fwdpy11/app/fwdpy11_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "gatk4-spark", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/gatk4-spark.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_gatk4-spark/app/gatk4-spark_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "gene-trajectory-python", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/gene-trajectory-python.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_gene-trajectory-python/app/gene-trajectory-python_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "genoboo", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/genoboo.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_genoboo/app/genoboo_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "gffread", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/gffread.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_gffread/app/gffread_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "glimmerhmm", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/glimmerhmm.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_glimmerhmm/app/glimmerhmm_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "gofasta", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/gofasta.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_gofasta/app/gofasta_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "gseapy", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/gseapy.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_gseapy/app/gseapy_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "hicexplorer", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/hicexplorer.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_hicexplorer/app/hicexplorer_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "htseq", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/htseq.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_htseq/app/htseq_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "hyphy", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/hyphy.manual_bundle.txt", + "run_help_command": false, + "status": "failed", + "error": "autogenerate_mcp_tool failed: Gemini request failed across all models. gemini-2.5-pro: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-3-flash-preview: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-2.5-flash: exception HTTPSConnectionPool(host='generativelanguage.googleapis.com', port=443): Read timed out. (read timeout=180)", + "output_file": "", + "dockerfile_ready": false + }, + { + "tool_name": "infernal", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/infernal.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_infernal/app/infernal_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "iow", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/iow.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_iow/app/iow_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "itsxpress", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/itsxpress.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_itsxpress/app/itsxpress_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "jbrowse2", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/jbrowse2.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_jbrowse2/app/jbrowse2_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "k8", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/k8.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_k8/app/k8_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "kb-python", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/kb-python.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_kb-python/app/kb-python_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "kmer-jellyfish", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/kmer-jellyfish.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_kmer-jellyfish/app/kmer-jellyfish_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "lastz", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/lastz.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_lastz/app/lastz_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "libdeflate", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/libdeflate.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_libdeflate/app/libdeflate_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "mafft", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/mafft.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_mafft/app/mafft_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "markerrepo", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/markerrepo.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_markerrepo/app/markerrepo_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "medaka", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/medaka.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_medaka/app/medaka_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "metagenome-atlas", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/metagenome-atlas.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_metagenome-atlas/app/metagenome-atlas_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "minced", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/minced.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_minced/app/minced_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "mitos", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/mitos.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_mitos/app/mitos_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "mobivision-m", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/mobivision-m.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_mobivision-m/app/mobivision-m_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "msisensor-pro", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/msisensor-pro.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_msisensor-pro/app/msisensor-pro_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "multiqc-bcbio", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/multiqc-bcbio.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_multiqc-bcbio/app/multiqc-bcbio_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "muscle", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/muscle.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_muscle/app/muscle_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "nanoget", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/nanoget.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_nanoget/app/nanoget_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "ncbi-amrfinderplus", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/ncbi-amrfinderplus.manual_bundle.txt", + "run_help_command": false, + "status": "failed", + "error": "autogenerate_mcp_tool failed: Gemini request failed across all models. gemini-2.5-pro: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-3-flash-preview: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-2.5-flash: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n", + "output_file": "", + "dockerfile_ready": false + }, + { + "tool_name": "ncbi-vdb", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/ncbi-vdb.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_ncbi-vdb/app/ncbi-vdb_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "nextflow", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/nextflow.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_nextflow/app/nextflow_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "ngs-smap", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/ngs-smap.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_ngs-smap/app/ngs-smap_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "ont-fast5-api", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/ont-fast5-api.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_ont-fast5-api/app/ont-fast5-api_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "ourotools", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/ourotools.manual_bundle.txt", + "run_help_command": false, + "status": "failed", + "error": "autogenerate_mcp_tool failed: Gemini request failed across all models. gemini-2.5-pro: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-3-flash-preview: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-2.5-flash: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n", + "output_file": "", + "dockerfile_ready": false + }, + { + "tool_name": "pandaseq", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/pandaseq.manual_bundle.txt", + "run_help_command": false, + "status": "failed", + "error": "autogenerate_mcp_tool failed: Gemini request failed across all models. gemini-2.5-pro: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-3-flash-preview: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-2.5-flash: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n", + "output_file": "", + "dockerfile_ready": false + }, + { + "tool_name": "parallel", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/parallel.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_parallel/app/parallel_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "pbmm2", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/pbmm2.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_pbmm2/app/pbmm2_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-aceperl", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-aceperl.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-aceperl/app/perl-aceperl_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-alien-libxml2", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-alien-libxml2.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-alien-libxml2/app/perl-alien-libxml2_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-archive-tar", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-archive-tar.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-archive-tar/app/perl-archive-tar_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-bio-asn1-entrezgene", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-bio-asn1-entrezgene.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-bio-asn1-entrezgene/app/perl-bio-asn1-entrezgene_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-bio-phylo", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-bio-phylo.manual_bundle.txt", + "run_help_command": false, + "status": "failed", + "error": "autogenerate_mcp_tool failed: Gemini request failed across all models. gemini-2.5-pro: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-3-flash-preview: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-2.5-flash: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n", + "output_file": "", + "dockerfile_ready": false + }, + { + "tool_name": "perl-bio-tools-run-alignment-clustalw", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-bio-tools-run-alignment-clustalw.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-bio-tools-run-alignment-clustalw/app/perl-bio-tools-run-alignment-clustalw_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-bioperl-run", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-bioperl-run.manual_bundle.txt", + "run_help_command": false, + "status": "failed", + "error": "autogenerate_mcp_tool failed: Gemini request failed across all models. gemini-2.5-pro: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-3-flash-preview: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-2.5-flash: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n", + "output_file": "", + "dockerfile_ready": false + }, + { + "tool_name": "perl-capture-tiny", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-capture-tiny.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-capture-tiny/app/perl-capture-tiny_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-class-inspector", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-class-inspector.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-class-inspector/app/perl-class-inspector_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-clone", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-clone.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-clone/app/perl-clone_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-config-general", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-config-general.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-config-general/app/perl-config-general_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-cpan-meta", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-cpan-meta.manual_bundle.txt", + "run_help_command": false, + "status": "failed", + "error": "autogenerate_mcp_tool failed: Gemini request failed across all models. gemini-2.5-pro: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-3-flash-preview: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-2.5-flash: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n", + "output_file": "", + "dockerfile_ready": false + }, + { + "tool_name": "perl-data-optlist", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-data-optlist.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-data-optlist/app/perl-data-optlist_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-dbd-mysql", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-dbd-mysql.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-dbd-mysql/app/perl-dbd-mysql_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-devel-overloadinfo", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-devel-overloadinfo.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-devel-overloadinfo/app/perl-devel-overloadinfo_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-digest-perl-md5", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-digest-perl-md5.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-digest-perl-md5/app/perl-digest-perl-md5_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-email-date-format", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-email-date-format.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-email-date-format/app/perl-email-date-format_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-eval-closure", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-eval-closure.manual_bundle.txt", + "run_help_command": false, + "status": "failed", + "error": "autogenerate_mcp_tool failed: Gemini request failed across all models. gemini-2.5-pro: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-3-flash-preview: exception HTTPSConnectionPool(host='generativelanguage.googleapis.com', port=443): Read timed out. (read timeout=180) | gemini-2.5-flash: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n", + "output_file": "", + "dockerfile_ready": false + }, + { + "tool_name": "perl-extutils-cbuilder", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-extutils-cbuilder.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-extutils-cbuilder/app/perl-extutils-cbuilder_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-ffi-checklib", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-ffi-checklib.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-ffi-checklib/app/perl-ffi-checklib_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-file-sort", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-file-sort.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-file-sort/app/perl-file-sort_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-font-afm", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-font-afm.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-font-afm/app/perl-font-afm_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-graph", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-graph.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-graph/app/perl-graph_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-html-formatter", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-html-formatter.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-html-formatter/app/perl-html-formatter_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-html-tidy", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-html-tidy.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-html-tidy/app/perl-html-tidy_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-http-daemon", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-http-daemon.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-http-daemon/app/perl-http-daemon_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-image-info", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-image-info.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-image-info/app/perl-image-info_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-io-html", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-io-html.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-io-html/app/perl-io-html_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-io-stringy", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-io-stringy.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-io-stringy/app/perl-io-stringy_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-ipc-run", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-ipc-run.manual_bundle.txt", + "run_help_command": false, + "status": "failed", + "error": "refine failed (retry=1): Gemini request failed across all models. gemini-2.5-pro: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-3-flash-preview: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-2.5-flash: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n", + "output_file": "", + "dockerfile_ready": false + }, + { + "tool_name": "perl-json-pp", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-json-pp.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-json-pp/app/perl-json-pp_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-libxml-perl", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-libxml-perl.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-libxml-perl/app/perl-libxml-perl_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-lwp-mediatypes", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-lwp-mediatypes.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-lwp-mediatypes/app/perl-lwp-mediatypes_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-math-bezier", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-math-bezier.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-math-bezier/app/perl-math-bezier_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-math-round", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-math-round.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-math-round/app/perl-math-round_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-mime-base64", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-mime-base64.manual_bundle.txt", + "run_help_command": false, + "status": "failed", + "error": "autogenerate_mcp_tool failed: Gemini request failed across all models. gemini-2.5-pro: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-3-flash-preview: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-2.5-flash: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n", + "output_file": "", + "dockerfile_ready": false + }, + { + "tool_name": "perl-mldbm", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-mldbm.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-mldbm/app/perl-mldbm_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-module-load", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-module-load.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-module-load/app/perl-module-load_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-module-runtime-conflicts", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-module-runtime-conflicts.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-module-runtime-conflicts/app/perl-module-runtime-conflicts_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-mro-compat", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-mro-compat.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-mro-compat/app/perl-mro-compat_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-number-format", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-number-format.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-number-format/app/perl-number-format_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-package-stash-xs", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-package-stash-xs.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-package-stash-xs/app/perl-package-stash-xs_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-params-validate", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-params-validate.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-params-validate/app/perl-params-validate_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-pathtools", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-pathtools.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-pathtools/app/perl-pathtools_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-pod-usage", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-pod-usage.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-pod-usage/app/perl-pod-usage_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-role-tiny", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-role-tiny.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-role-tiny/app/perl-role-tiny_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-sereal-encoder", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-sereal-encoder.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-sereal-encoder/app/perl-sereal-encoder_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-soap-lite", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-soap-lite.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-soap-lite/app/perl-soap-lite_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-spreadsheet-writeexcel", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-spreadsheet-writeexcel.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-spreadsheet-writeexcel/app/perl-spreadsheet-writeexcel_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-sub-exporter", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-sub-exporter.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-sub-exporter/app/perl-sub-exporter_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-sub-name", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-sub-name.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-sub-name/app/perl-sub-name_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-svg-graph", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-svg-graph.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-svg-graph/app/perl-svg-graph_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-test", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-test.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-test/app/perl-test_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-test-fatal", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-test-fatal.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-test-fatal/app/perl-test-fatal_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-test-most", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-test-most.manual_bundle.txt", + "run_help_command": false, + "status": "failed", + "error": "autogenerate_mcp_tool failed: Gemini request failed across all models. gemini-2.5-pro: HTTP 429 - {\n \"error\": {\n \"code\": 429,\n \"message\": \"You exceeded your current quota, please check your plan and billing details. For more information on this error, head to: https://ai.google.dev/gemini-api/docs/rate-limits. To monitor your current usage, head to: https://ai.dev/rate-limit. \\n* Quota exceeded for metric: generativelanguage.googleapis.com/generate_requests_per_model_per_day, limit: 1000, model: gemini-2.5-pro\\nPlease retry in 9h51m15.887251046s.\",\n \"status\": \"RESOURCE_EXHAUSTED\",\n \"details\": [\n {\n \"@type\": \"type.googleapis.com/google.rpc.Help\",\n \"links\": [\n {\n \"description\": \"Learn more about Gemini API quotas\",\n \"url\": \"https://ai.google.dev/gemini-api/docs/rate-limits\"\n }\n ]\n },\n {\n \"@type\": \"type.googleapis.com/google.rpc.QuotaFailure\",\n \"violations\": [\n {\n \"quotaMetric\": \"generativelanguage.googleapis.com/generate_requests_per_model_per_day\",\n \"quotaId\": \"GenerateRequestsPerDayPerProjectPerModel\",\n \"quotaDimensions\": {\n \"model\": \"gemini-2.5-pro\",\n \"location\": \"global\"\n },\n \"quotaValu | gemini-3-flash-preview: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-2.5-flash: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n", + "output_file": "", + "dockerfile_ready": false + }, + { + "tool_name": "perl-text-csv", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-text-csv.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-text-csv/app/perl-text-csv_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-threaded", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-threaded.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-threaded/app/perl-threaded_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-timedate", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-timedate.manual_bundle.txt", + "run_help_command": false, + "status": "failed", + "error": "autogenerate_mcp_tool failed: Gemini request failed across all models. gemini-2.5-pro: HTTP 429 - {\n \"error\": {\n \"code\": 429,\n \"message\": \"You exceeded your current quota, please check your plan and billing details. For more information on this error, head to: https://ai.google.dev/gemini-api/docs/rate-limits. To monitor your current usage, head to: https://ai.dev/rate-limit. \\n* Quota exceeded for metric: generativelanguage.googleapis.com/generate_requests_per_model_per_day, limit: 1000, model: gemini-2.5-pro\\nPlease retry in 9h45m42.039969527s.\",\n \"status\": \"RESOURCE_EXHAUSTED\",\n \"details\": [\n {\n \"@type\": \"type.googleapis.com/google.rpc.Help\",\n \"links\": [\n {\n \"description\": \"Learn more about Gemini API quotas\",\n \"url\": \"https://ai.google.dev/gemini-api/docs/rate-limits\"\n }\n ]\n },\n {\n \"@type\": \"type.googleapis.com/google.rpc.QuotaFailure\",\n \"violations\": [\n {\n \"quotaMetric\": \"generativelanguage.googleapis.com/generate_requests_per_model_per_day\",\n \"quotaId\": \"GenerateRequestsPerDayPerProjectPerModel\",\n \"quotaDimensions\": {\n \"location\": \"global\",\n \"model\": \"gemini-2.5-pro\"\n },\n \"quotaValu | gemini-3-flash-preview: exception HTTPSConnectionPool(host='generativelanguage.googleapis.com', port=443): Read timed out. (read timeout=180) | gemini-2.5-flash: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n", + "output_file": "", + "dockerfile_ready": false + }, + { + "tool_name": "perl-types-serialiser", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-types-serialiser.manual_bundle.txt", + "run_help_command": false, + "status": "failed", + "error": "autogenerate_mcp_tool failed: Gemini request failed across all models. gemini-2.5-pro: HTTP 429 - {\n \"error\": {\n \"code\": 429,\n \"message\": \"You exceeded your current quota, please check your plan and billing details. For more information on this error, head to: https://ai.google.dev/gemini-api/docs/rate-limits. To monitor your current usage, head to: https://ai.dev/rate-limit. \\n* Quota exceeded for metric: generativelanguage.googleapis.com/generate_requests_per_model_per_day, limit: 1000, model: gemini-2.5-pro\\nPlease retry in 9h42m40.957250164s.\",\n \"status\": \"RESOURCE_EXHAUSTED\",\n \"details\": [\n {\n \"@type\": \"type.googleapis.com/google.rpc.Help\",\n \"links\": [\n {\n \"description\": \"Learn more about Gemini API quotas\",\n \"url\": \"https://ai.google.dev/gemini-api/docs/rate-limits\"\n }\n ]\n },\n {\n \"@type\": \"type.googleapis.com/google.rpc.QuotaFailure\",\n \"violations\": [\n {\n \"quotaMetric\": \"generativelanguage.googleapis.com/generate_requests_per_model_per_day\",\n \"quotaId\": \"GenerateRequestsPerDayPerProjectPerModel\",\n \"quotaDimensions\": {\n \"model\": \"gemini-2.5-pro\",\n \"location\": \"global\"\n },\n \"quotaValu | gemini-3-flash-preview: exception HTTPSConnectionPool(host='generativelanguage.googleapis.com', port=443): Read timed out. (read timeout=180) | gemini-2.5-flash: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n", + "output_file": "", + "dockerfile_ready": false + }, + { + "tool_name": "perl-version", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-version.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-version/app/perl-version_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-xml-filter-buffertext", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-xml-filter-buffertext.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-xml-filter-buffertext/app/perl-xml-filter-buffertext_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-xml-parser", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-xml-parser.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-xml-parser/app/perl-xml-parser_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-xml-sax-expat", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-xml-sax-expat.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-xml-sax-expat/app/perl-xml-sax-expat_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-xml-writer", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-xml-writer.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-xml-writer/app/perl-xml-writer_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-yaml", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-yaml.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-yaml/app/perl-yaml_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "picard-slim", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/picard-slim.manual_bundle.txt", + "run_help_command": false, + "status": "failed", + "error": "autogenerate_mcp_tool failed: Gemini request failed across all models. gemini-2.5-pro: HTTP 429 - {\n \"error\": {\n \"code\": 429,\n \"message\": \"You exceeded your current quota, please check your plan and billing details. For more information on this error, head to: https://ai.google.dev/gemini-api/docs/rate-limits. To monitor your current usage, head to: https://ai.dev/rate-limit. \\n* Quota exceeded for metric: generativelanguage.googleapis.com/generate_requests_per_model_per_day, limit: 1000, model: gemini-2.5-pro\\nPlease retry in 9h27m15.572366879s.\",\n \"status\": \"RESOURCE_EXHAUSTED\",\n \"details\": [\n {\n \"@type\": \"type.googleapis.com/google.rpc.Help\",\n \"links\": [\n {\n \"description\": \"Learn more about Gemini API quotas\",\n \"url\": \"https://ai.google.dev/gemini-api/docs/rate-limits\"\n }\n ]\n },\n {\n \"@type\": \"type.googleapis.com/google.rpc.QuotaFailure\",\n \"violations\": [\n {\n \"quotaMetric\": \"generativelanguage.googleapis.com/generate_requests_per_model_per_day\",\n \"quotaId\": \"GenerateRequestsPerDayPerProjectPerModel\",\n \"quotaDimensions\": {\n \"location\": \"global\",\n \"model\": \"gemini-2.5-pro\"\n },\n \"quotaValu | gemini-3-flash-preview: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-2.5-flash: exception HTTPSConnectionPool(host='generativelanguage.googleapis.com', port=443): Read timed out. (read timeout=180)", + "output_file": "", + "dockerfile_ready": false + }, + { + "tool_name": "plink", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/plink.manual_bundle.txt", + "run_help_command": false, + "status": "failed", + "error": "autogenerate_mcp_tool failed: Gemini request failed across all models. gemini-2.5-pro: HTTP 429 - {\n \"error\": {\n \"code\": 429,\n \"message\": \"You exceeded your current quota, please check your plan and billing details. For more information on this error, head to: https://ai.google.dev/gemini-api/docs/rate-limits. To monitor your current usage, head to: https://ai.dev/rate-limit. \\n* Quota exceeded for metric: generativelanguage.googleapis.com/generate_requests_per_model_per_day, limit: 1000, model: gemini-2.5-pro\\nPlease retry in 9h24m14.732505327s.\",\n \"status\": \"RESOURCE_EXHAUSTED\",\n \"details\": [\n {\n \"@type\": \"type.googleapis.com/google.rpc.Help\",\n \"links\": [\n {\n \"description\": \"Learn more about Gemini API quotas\",\n \"url\": \"https://ai.google.dev/gemini-api/docs/rate-limits\"\n }\n ]\n },\n {\n \"@type\": \"type.googleapis.com/google.rpc.QuotaFailure\",\n \"violations\": [\n {\n \"quotaMetric\": \"generativelanguage.googleapis.com/generate_requests_per_model_per_day\",\n \"quotaId\": \"GenerateRequestsPerDayPerProjectPerModel\",\n \"quotaDimensions\": {\n \"location\": \"global\",\n \"model\": \"gemini-2.5-pro\"\n },\n \"quotaValu | gemini-3-flash-preview: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-2.5-flash: exception HTTPSConnectionPool(host='generativelanguage.googleapis.com', port=443): Read timed out. (read timeout=180)", + "output_file": "", + "dockerfile_ready": false + }, + { + "tool_name": "prank", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/prank.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_prank/app/prank_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "prokka", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/prokka.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_prokka/app/prokka_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "proteomiqon-peptidespectrummatching", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/proteomiqon-peptidespectrummatching.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_proteomiqon-peptidespectrummatching/app/proteomiqon-peptidespectrummatching_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "pybigwig", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/pybigwig.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_pybigwig/app/pybigwig_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "pyhmmer", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/pyhmmer.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_pyhmmer/app/pyhmmer_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "pyrovelocity", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/pyrovelocity.manual_bundle.txt", + "run_help_command": false, + "status": "failed", + "error": "autogenerate_mcp_tool failed: Gemini request failed across all models. gemini-2.5-pro: HTTP 429 - {\n \"error\": {\n \"code\": 429,\n \"message\": \"You exceeded your current quota, please check your plan and billing details. For more information on this error, head to: https://ai.google.dev/gemini-api/docs/rate-limits. To monitor your current usage, head to: https://ai.dev/rate-limit. \\n* Quota exceeded for metric: generativelanguage.googleapis.com/generate_requests_per_model_per_day, limit: 1000, model: gemini-2.5-pro\\nPlease retry in 9h13m42.171564832s.\",\n \"status\": \"RESOURCE_EXHAUSTED\",\n \"details\": [\n {\n \"@type\": \"type.googleapis.com/google.rpc.Help\",\n \"links\": [\n {\n \"description\": \"Learn more about Gemini API quotas\",\n \"url\": \"https://ai.google.dev/gemini-api/docs/rate-limits\"\n }\n ]\n },\n {\n \"@type\": \"type.googleapis.com/google.rpc.QuotaFailure\",\n \"violations\": [\n {\n \"quotaMetric\": \"generativelanguage.googleapis.com/generate_requests_per_model_per_day\",\n \"quotaId\": \"GenerateRequestsPerDayPerProjectPerModel\",\n \"quotaDimensions\": {\n \"location\": \"global\",\n \"model\": \"gemini-2.5-pro\"\n },\n \"quotaValu | gemini-3-flash-preview: exception HTTPSConnectionPool(host='generativelanguage.googleapis.com', port=443): Read timed out. (read timeout=180) | gemini-2.5-flash: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n", + "output_file": "", + "dockerfile_ready": false + }, + { + "tool_name": "pytabix", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/pytabix.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_pytabix/app/pytabix_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "pyvcf3", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/pyvcf3.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_pyvcf3/app/pyvcf3_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "quast", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/quast.manual_bundle.txt", + "run_help_command": false, + "status": "failed", + "error": "autogenerate_mcp_tool failed: Gemini request failed across all models. gemini-2.5-pro: HTTP 429 - {\n \"error\": {\n \"code\": 429,\n \"message\": \"You exceeded your current quota, please check your plan and billing details. For more information on this error, head to: https://ai.google.dev/gemini-api/docs/rate-limits. To monitor your current usage, head to: https://ai.dev/rate-limit. \\n* Quota exceeded for metric: generativelanguage.googleapis.com/generate_requests_per_model_per_day, limit: 1000, model: gemini-2.5-pro\\nPlease retry in 9h9m17.423485061s.\",\n \"status\": \"RESOURCE_EXHAUSTED\",\n \"details\": [\n {\n \"@type\": \"type.googleapis.com/google.rpc.Help\",\n \"links\": [\n {\n \"description\": \"Learn more about Gemini API quotas\",\n \"url\": \"https://ai.google.dev/gemini-api/docs/rate-limits\"\n }\n ]\n },\n {\n \"@type\": \"type.googleapis.com/google.rpc.QuotaFailure\",\n \"violations\": [\n {\n \"quotaMetric\": \"generativelanguage.googleapis.com/generate_requests_per_model_per_day\",\n \"quotaId\": \"GenerateRequestsPerDayPerProjectPerModel\",\n \"quotaDimensions\": {\n \"location\": \"global\",\n \"model\": \"gemini-2.5-pro\"\n },\n \"quotaValue | gemini-3-flash-preview: exception HTTPSConnectionPool(host='generativelanguage.googleapis.com', port=443): Read timed out. (read timeout=180) | gemini-2.5-flash: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n", + "output_file": "", + "dockerfile_ready": false + }, + { + "tool_name": "r-azimuth", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/r-azimuth.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_r-azimuth/app/r-azimuth_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "r-dwls", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/r-dwls.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_r-dwls/app/r-dwls_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "r-mams", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/r-mams.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_r-mams/app/r-mams_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "r-sceasy", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/r-sceasy.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_r-sceasy/app/r-sceasy_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "racon", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/racon.manual_bundle.txt", + "run_help_command": false, + "status": "failed", + "error": "autogenerate_mcp_tool failed: Gemini request failed across all models. gemini-2.5-pro: HTTP 429 - {\n \"error\": {\n \"code\": 429,\n \"message\": \"You exceeded your current quota, please check your plan and billing details. For more information on this error, head to: https://ai.google.dev/gemini-api/docs/rate-limits. To monitor your current usage, head to: https://ai.dev/rate-limit. \\n* Quota exceeded for metric: generativelanguage.googleapis.com/generate_requests_per_model_per_day, limit: 1000, model: gemini-2.5-pro\\nPlease retry in 9h6m16.136744437s.\",\n \"status\": \"RESOURCE_EXHAUSTED\",\n \"details\": [\n {\n \"@type\": \"type.googleapis.com/google.rpc.Help\",\n \"links\": [\n {\n \"description\": \"Learn more about Gemini API quotas\",\n \"url\": \"https://ai.google.dev/gemini-api/docs/rate-limits\"\n }\n ]\n },\n {\n \"@type\": \"type.googleapis.com/google.rpc.QuotaFailure\",\n \"violations\": [\n {\n \"quotaMetric\": \"generativelanguage.googleapis.com/generate_requests_per_model_per_day\",\n \"quotaId\": \"GenerateRequestsPerDayPerProjectPerModel\",\n \"quotaDimensions\": {\n \"location\": \"global\",\n \"model\": \"gemini-2.5-pro\"\n },\n \"quotaValue | gemini-3-flash-preview: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-2.5-flash: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n", + "output_file": "", + "dockerfile_ready": false + }, + { + "tool_name": "rgi", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/rgi.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_rgi/app/rgi_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "rsa", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/rsa.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_rsa/app/rsa_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "rtg-tools", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/rtg-tools.manual_bundle.txt", + "run_help_command": false, + "status": "failed", + "error": "autogenerate_mcp_tool failed: Gemini request failed across all models. gemini-2.5-pro: HTTP 429 - {\n \"error\": {\n \"code\": 429,\n \"message\": \"You exceeded your current quota, please check your plan and billing details. For more information on this error, head to: https://ai.google.dev/gemini-api/docs/rate-limits. To monitor your current usage, head to: https://ai.dev/rate-limit. \\n* Quota exceeded for metric: generativelanguage.googleapis.com/generate_requests_per_model_per_day, limit: 1000, model: gemini-2.5-pro\\nPlease retry in 9h5m22.933769627s.\",\n \"status\": \"RESOURCE_EXHAUSTED\",\n \"details\": [\n {\n \"@type\": \"type.googleapis.com/google.rpc.Help\",\n \"links\": [\n {\n \"description\": \"Learn more about Gemini API quotas\",\n \"url\": \"https://ai.google.dev/gemini-api/docs/rate-limits\"\n }\n ]\n },\n {\n \"@type\": \"type.googleapis.com/google.rpc.QuotaFailure\",\n \"violations\": [\n {\n \"quotaMetric\": \"generativelanguage.googleapis.com/generate_requests_per_model_per_day\",\n \"quotaId\": \"GenerateRequestsPerDayPerProjectPerModel\",\n \"quotaDimensions\": {\n \"location\": \"global\",\n \"model\": \"gemini-2.5-pro\"\n },\n \"quotaValue | gemini-3-flash-preview: exception HTTPSConnectionPool(host='generativelanguage.googleapis.com', port=443): Read timed out. (read timeout=180) | gemini-2.5-flash: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n", + "output_file": "", + "dockerfile_ready": false + }, + { + "tool_name": "samap", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/samap.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_samap/app/samap_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "samtools", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/samtools.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_samtools/app/samtools_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "scanpy-scripts", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/scanpy-scripts.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_scanpy-scripts/app/scanpy-scripts_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "scib", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/scib.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_scib/app/scib_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "scspectra", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/scspectra.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_scspectra/app/scspectra_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "scvi-tools", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/scvi-tools.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_scvi-tools/app/scvi-tools_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "sdeper", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/sdeper.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_sdeper/app/sdeper_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "sentieon", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/sentieon.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_sentieon/app/sentieon_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "seqfu", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/seqfu.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_seqfu/app/seqfu_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "shiba", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/shiba.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_shiba/app/shiba_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "sincei", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/sincei.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_sincei/app/sincei_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "snakemake-interface-executor-plugins", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/snakemake-interface-executor-plugins.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_snakemake-interface-executor-plugins/app/snakemake-interface-executor-plugins_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "snakemake-minimal", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/snakemake-minimal.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_snakemake-minimal/app/snakemake-minimal_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "snippy", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/snippy.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_snippy/app/snippy_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "sorted_nearest", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/sorted_nearest.manual_bundle.txt", + "run_help_command": false, + "status": "failed", + "error": "regenerate failed (retry=1): Gemini request failed across all models. gemini-2.5-pro: HTTP 429 - {\n \"error\": {\n \"code\": 429,\n \"message\": \"You exceeded your current quota, please check your plan and billing details. For more information on this error, head to: https://ai.google.dev/gemini-api/docs/rate-limits. To monitor your current usage, head to: https://ai.dev/rate-limit. \\n* Quota exceeded for metric: generativelanguage.googleapis.com/generate_requests_per_model_per_day, limit: 1000, model: gemini-2.5-pro\\nPlease retry in 8h55m13.943597057s.\",\n \"status\": \"RESOURCE_EXHAUSTED\",\n \"details\": [\n {\n \"@type\": \"type.googleapis.com/google.rpc.Help\",\n \"links\": [\n {\n \"description\": \"Learn more about Gemini API quotas\",\n \"url\": \"https://ai.google.dev/gemini-api/docs/rate-limits\"\n }\n ]\n },\n {\n \"@type\": \"type.googleapis.com/google.rpc.QuotaFailure\",\n \"violations\": [\n {\n \"quotaMetric\": \"generativelanguage.googleapis.com/generate_requests_per_model_per_day\",\n \"quotaId\": \"GenerateRequestsPerDayPerProjectPerModel\",\n \"quotaDimensions\": {\n \"model\": \"gemini-2.5-pro\",\n \"location\": \"global\"\n },\n \"quotaValu | gemini-3-flash-preview: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-2.5-flash: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n", + "output_file": "", + "dockerfile_ready": false + }, + { + "tool_name": "spagrn", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/spagrn.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_spagrn/app/spagrn_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "stacks", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/stacks.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_stacks/app/stacks_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "stringtie", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/stringtie.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_stringtie/app/stringtie_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "t_coffee", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/t_coffee.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_t_coffee/app/t_coffee_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "taxonkit", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/taxonkit.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_taxonkit/app/taxonkit_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "tirank", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/tirank.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_tirank/app/tirank_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "transdecoder", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/transdecoder.manual_bundle.txt", + "run_help_command": false, + "status": "failed", + "error": "autogenerate_mcp_tool failed: Gemini request failed across all models. gemini-2.5-pro: HTTP 429 - {\n \"error\": {\n \"code\": 429,\n \"message\": \"You exceeded your current quota, please check your plan and billing details. For more information on this error, head to: https://ai.google.dev/gemini-api/docs/rate-limits. To monitor your current usage, head to: https://ai.dev/rate-limit. \\n* Quota exceeded for metric: generativelanguage.googleapis.com/generate_requests_per_model_per_day, limit: 1000, model: gemini-2.5-pro\\nPlease retry in 8h53m24.923764837s.\",\n \"status\": \"RESOURCE_EXHAUSTED\",\n \"details\": [\n {\n \"@type\": \"type.googleapis.com/google.rpc.Help\",\n \"links\": [\n {\n \"description\": \"Learn more about Gemini API quotas\",\n \"url\": \"https://ai.google.dev/gemini-api/docs/rate-limits\"\n }\n ]\n },\n {\n \"@type\": \"type.googleapis.com/google.rpc.QuotaFailure\",\n \"violations\": [\n {\n \"quotaMetric\": \"generativelanguage.googleapis.com/generate_requests_per_model_per_day\",\n \"quotaId\": \"GenerateRequestsPerDayPerProjectPerModel\",\n \"quotaDimensions\": {\n \"location\": \"global\",\n \"model\": \"gemini-2.5-pro\"\n },\n \"quotaValu | gemini-3-flash-preview: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-2.5-flash: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n", + "output_file": "", + "dockerfile_ready": false + }, + { + "tool_name": "trimadap", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/trimadap.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_trimadap/app/trimadap_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "trnascan-se", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/trnascan-se.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_trnascan-se/app/trnascan-se_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "ucsc-fatotwobit", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/ucsc-fatotwobit.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_ucsc-fatotwobit/app/ucsc-fatotwobit_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "ucsc-twobitinfo", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/ucsc-twobitinfo.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_ucsc-twobitinfo/app/ucsc-twobitinfo_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "umis", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/umis.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_umis/app/umis_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "upimapi", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/upimapi.manual_bundle.txt", + "run_help_command": false, + "status": "failed", + "error": "autogenerate_mcp_tool failed: Gemini request failed across all models. gemini-2.5-pro: HTTP 429 - {\n \"error\": {\n \"code\": 429,\n \"message\": \"You exceeded your current quota, please check your plan and billing details. For more information on this error, head to: https://ai.google.dev/gemini-api/docs/rate-limits. To monitor your current usage, head to: https://ai.dev/rate-limit. \\n* Quota exceeded for metric: generativelanguage.googleapis.com/generate_requests_per_model_per_day, limit: 1000, model: gemini-2.5-pro\\nPlease retry in 8h51m50.323066157s.\",\n \"status\": \"RESOURCE_EXHAUSTED\",\n \"details\": [\n {\n \"@type\": \"type.googleapis.com/google.rpc.Help\",\n \"links\": [\n {\n \"description\": \"Learn more about Gemini API quotas\",\n \"url\": \"https://ai.google.dev/gemini-api/docs/rate-limits\"\n }\n ]\n },\n {\n \"@type\": \"type.googleapis.com/google.rpc.QuotaFailure\",\n \"violations\": [\n {\n \"quotaMetric\": \"generativelanguage.googleapis.com/generate_requests_per_model_per_day\",\n \"quotaId\": \"GenerateRequestsPerDayPerProjectPerModel\",\n \"quotaDimensions\": {\n \"model\": \"gemini-2.5-pro\",\n \"location\": \"global\"\n },\n \"quotaValu | gemini-3-flash-preview: exception HTTPSConnectionPool(host='generativelanguage.googleapis.com', port=443): Read timed out. (read timeout=180) | gemini-2.5-flash: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n", + "output_file": "", + "dockerfile_ready": false + }, + { + "tool_name": "vardict-java", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/vardict-java.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_vardict-java/app/vardict-java_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "vcfpy", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/vcfpy.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_vcfpy/app/vcfpy_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "vitessce-python", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/vitessce-python.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_vitessce-python/app/vitessce-python_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "wasp2", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/wasp2.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_wasp2/app/wasp2_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "xopen", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/xopen.manual_bundle.txt", + "run_help_command": false, + "status": "failed", + "error": "autogenerate_mcp_tool failed: Gemini request failed across all models. gemini-2.5-pro: HTTP 429 - {\n \"error\": {\n \"code\": 429,\n \"message\": \"You exceeded your current quota, please check your plan and billing details. For more information on this error, head to: https://ai.google.dev/gemini-api/docs/rate-limits. To monitor your current usage, head to: https://ai.dev/rate-limit. \\n* Quota exceeded for metric: generativelanguage.googleapis.com/generate_requests_per_model_per_day, limit: 1000, model: gemini-2.5-pro\\nPlease retry in 8h46m55.753926583s.\",\n \"status\": \"RESOURCE_EXHAUSTED\",\n \"details\": [\n {\n \"@type\": \"type.googleapis.com/google.rpc.Help\",\n \"links\": [\n {\n \"description\": \"Learn more about Gemini API quotas\",\n \"url\": \"https://ai.google.dev/gemini-api/docs/rate-limits\"\n }\n ]\n },\n {\n \"@type\": \"type.googleapis.com/google.rpc.QuotaFailure\",\n \"violations\": [\n {\n \"quotaMetric\": \"generativelanguage.googleapis.com/generate_requests_per_model_per_day\",\n \"quotaId\": \"GenerateRequestsPerDayPerProjectPerModel\",\n \"quotaDimensions\": {\n \"location\": \"global\",\n \"model\": \"gemini-2.5-pro\"\n },\n \"quotaValu | gemini-3-flash-preview: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-2.5-flash: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n", + "output_file": "", + "dockerfile_ready": false + } +] \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/conversion_report.shard_1_of_4.json b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/conversion_report.shard_1_of_4.json new file mode 100644 index 0000000000000000000000000000000000000000..eca51e888e0e640dec59b661461046afe65e17cd --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/conversion_report.shard_1_of_4.json @@ -0,0 +1,2108 @@ +[ + { + "tool_name": "abricate", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/abricate.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_abricate/app/abricate_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "anansescanpy", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/anansescanpy.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_anansescanpy/app/anansescanpy_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "ariba", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/ariba.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_ariba/app/ariba_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "arvados-python-client", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/arvados-python-client.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_arvados-python-client/app/arvados-python-client_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bactopia", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bactopia.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bactopia/app/bactopia_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bedops", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bedops.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bedops/app/bedops_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioblend", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioblend.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioblend/app/bioblend_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-affyio", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-affyio.manual_bundle.txt", + "run_help_command": false, + "status": "failed", + "error": "autogenerate_mcp_tool failed: Gemini request failed across all models. gemini-2.5-pro: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-3-flash-preview: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-2.5-flash: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n", + "output_file": "", + "dockerfile_ready": false + }, + { + "tool_name": "bioconductor-ancombc", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-ancombc.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-ancombc/app/bioconductor-ancombc_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-awaggregator", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-awaggregator.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-awaggregator/app/bioconductor-awaggregator_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-biocfilecache", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-biocfilecache.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-biocfilecache/app/bioconductor-biocfilecache_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-blase", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-blase.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-blase/app/bioconductor-blase_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-cardspa", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-cardspa.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-cardspa/app/bioconductor-cardspa_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-cellid", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-cellid.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-cellid/app/bioconductor-cellid_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-clustsignal", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-clustsignal.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-clustsignal/app/bioconductor-clustsignal_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-csoa", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-csoa.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-csoa/app/bioconductor-csoa_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-data-packages", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-data-packages.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-data-packages/app/bioconductor-data-packages_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-deconvobuddies", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-deconvobuddies.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-deconvobuddies/app/bioconductor-deconvobuddies_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-despace", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-despace.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-despace/app/bioconductor-despace_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-dnacopy", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-dnacopy.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-dnacopy/app/bioconductor-dnacopy_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-ensembldb", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-ensembldb.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-ensembldb/app/bioconductor-ensembldb_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-fgsea", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-fgsea.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-fgsea/app/bioconductor-fgsea_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-genomeinfodbdata", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-genomeinfodbdata.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-genomeinfodbdata/app/bioconductor-genomeinfodbdata_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-geomxtools", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-geomxtools.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-geomxtools/app/bioconductor-geomxtools_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-glmgampoi", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-glmgampoi.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-glmgampoi/app/bioconductor-glmgampoi_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-hcatonsildata", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-hcatonsildata.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-hcatonsildata/app/bioconductor-hcatonsildata_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-imcdatasets", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-imcdatasets.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-imcdatasets/app/bioconductor-imcdatasets_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-iranges", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-iranges.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-iranges/app/bioconductor-iranges_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-limma", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-limma.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-limma/app/bioconductor-limma_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-merfishdata", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-merfishdata.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-merfishdata/app/bioconductor-merfishdata_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-mousegastrulationdata", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-mousegastrulationdata.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-mousegastrulationdata/app/bioconductor-mousegastrulationdata_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-mzr", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-mzr.manual_bundle.txt", + "run_help_command": false, + "status": "failed", + "error": "autogenerate_mcp_tool failed: Gemini request failed across all models. gemini-2.5-pro: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-3-flash-preview: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-2.5-flash: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n", + "output_file": "", + "dockerfile_ready": false + }, + { + "tool_name": "bioconductor-org.ce.eg.db", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-org.ce.eg.db.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-org.ce.eg.db/app/bioconductor-org.ce.eg.db_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-phyloseq", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-phyloseq.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-phyloseq/app/bioconductor-phyloseq_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-protgenerics", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-protgenerics.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-protgenerics/app/bioconductor-protgenerics_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-regionalst", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-regionalst.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-regionalst/app/bioconductor-regionalst_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-rhdf5lib", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-rhdf5lib.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-rhdf5lib/app/bioconductor-rhdf5lib_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-rtracklayer", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-rtracklayer.manual_bundle.txt", + "run_help_command": false, + "status": "failed", + "error": "autogenerate_mcp_tool failed: Gemini request failed across all models. gemini-2.5-pro: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-3-flash-preview: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-2.5-flash: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n", + "output_file": "", + "dockerfile_ready": false + }, + { + "tool_name": "bioconductor-scalign", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-scalign.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-scalign/app/bioconductor-scalign_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-scbubbletree", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-scbubbletree.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-scbubbletree/app/bioconductor-scbubbletree_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-scider", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-scider.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-scider/app/bioconductor-scider_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-sctreeviz", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-sctreeviz.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-sctreeviz/app/bioconductor-sctreeviz_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-shortread", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-shortread.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-shortread/app/bioconductor-shortread_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-smoothclust", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-smoothclust.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-smoothclust/app/bioconductor-smoothclust_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-spacexr", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-spacexr.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-spacexr/app/bioconductor-spacexr_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-sparsematrixstats", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-sparsematrixstats.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-sparsematrixstats/app/bioconductor-sparsematrixstats_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-spatialdmelxsim", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-spatialdmelxsim.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-spatialdmelxsim/app/bioconductor-spatialdmelxsim_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-spatiallibd", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-spatiallibd.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-spatiallibd/app/bioconductor-spatiallibd_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-spiat", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-spiat.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-spiat/app/bioconductor-spiat_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-spotsweeper", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-spotsweeper.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-spotsweeper/app/bioconductor-spotsweeper_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-stjoincount", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-stjoincount.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-stjoincount/app/bioconductor-stjoincount_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-svp", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-svp.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-svp/app/bioconductor-svp_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-tidyomics", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-tidyomics.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-tidyomics/app/bioconductor-tidyomics_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-treesummarizedexperiment", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-treesummarizedexperiment.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-treesummarizedexperiment/app/bioconductor-treesummarizedexperiment_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-visiumio", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-visiumio.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-visiumio/app/bioconductor-visiumio_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-xcms", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-xcms.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-xcms/app/bioconductor-xcms_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-zlibbioc", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-zlibbioc.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-zlibbioc/app/bioconductor-zlibbioc_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "blast-legacy", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/blast-legacy.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_blast-legacy/app/blast-legacy_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "busco", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/busco.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_busco/app/busco_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "cascade-reg", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/cascade-reg.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_cascade-reg/app/cascade-reg_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "cellitac", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/cellitac.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_cellitac/app/cellitac_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "checkatlas", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/checkatlas.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_checkatlas/app/checkatlas_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "clustalo", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/clustalo.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_clustalo/app/clustalo_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "comebin", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/comebin.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_comebin/app/comebin_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "cooltools", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/cooltools.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_cooltools/app/cooltools_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "crispresso2", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/crispresso2.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_crispresso2/app/crispresso2_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "csvtk", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/csvtk.manual_bundle.txt", + "run_help_command": false, + "status": "failed", + "error": "autogenerate_mcp_tool failed: Gemini request failed across all models. gemini-2.5-pro: exception HTTPSConnectionPool(host='generativelanguage.googleapis.com', port=443): Read timed out. (read timeout=180) | gemini-3-flash-preview: exception HTTPSConnectionPool(host='generativelanguage.googleapis.com', port=443): Read timed out. (read timeout=180) | gemini-2.5-flash: exception HTTPSConnectionPool(host='generativelanguage.googleapis.com', port=443): Read timed out. (read timeout=180)", + "output_file": "", + "dockerfile_ready": false + }, + { + "tool_name": "cyvcf2", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/cyvcf2.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_cyvcf2/app/cyvcf2_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "deeptools", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/deeptools.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_deeptools/app/deeptools_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "diamond", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/diamond.manual_bundle.txt", + "run_help_command": false, + "status": "failed", + "error": "autogenerate_mcp_tool failed: Gemini request failed across all models. gemini-2.5-pro: exception HTTPSConnectionPool(host='generativelanguage.googleapis.com', port=443): Read timed out. (read timeout=180) | gemini-3-flash-preview: exception HTTPSConnectionPool(host='generativelanguage.googleapis.com', port=443): Read timed out. (read timeout=180) | gemini-2.5-flash: exception HTTPSConnectionPool(host='generativelanguage.googleapis.com', port=443): Read timed out. (read timeout=180)", + "output_file": "", + "dockerfile_ready": false + }, + { + "tool_name": "dsh-bio", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/dsh-bio.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_dsh-bio/app/dsh-bio_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "emboss", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/emboss.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_emboss/app/emboss_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "epic", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/epic.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_epic/app/epic_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "famsa", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/famsa.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_famsa/app/famsa_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "fastp", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/fastp.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_fastp/app/fastp_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "fgbio", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/fgbio.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_fgbio/app/fgbio_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "foldseek", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/foldseek.manual_bundle.txt", + "run_help_command": false, + "status": "failed", + "error": "refine failed (retry=3): Gemini request failed across all models. gemini-2.5-pro: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-3-flash-preview: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-2.5-flash: exception HTTPSConnectionPool(host='generativelanguage.googleapis.com', port=443): Read timed out. (read timeout=180)", + "output_file": "", + "dockerfile_ready": false + }, + { + "tool_name": "galaxy-lib", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/galaxy-lib.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_galaxy-lib/app/galaxy-lib_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "gdk-pixbuf", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/gdk-pixbuf.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_gdk-pixbuf/app/gdk-pixbuf_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "genecircuitry", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/genecircuitry.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_genecircuitry/app/genecircuitry_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "genomad", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/genomad.manual_bundle.txt", + "run_help_command": false, + "status": "failed", + "error": "autogenerate_mcp_tool failed: Gemini request failed across all models. gemini-2.5-pro: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-3-flash-preview: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-2.5-flash: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n", + "output_file": "", + "dockerfile_ready": false + }, + { + "tool_name": "gffutils", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/gffutils.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_gffutils/app/gffutils_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "gmap", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/gmap.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_gmap/app/gmap_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "goleft", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/goleft.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_goleft/app/goleft_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "gtdbtk", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/gtdbtk.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_gtdbtk/app/gtdbtk_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "hifiasm", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/hifiasm.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_hifiasm/app/hifiasm_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "htslib", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/htslib.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_htslib/app/htslib_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "igv", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/igv.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_igv/app/igv_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "insilicoseq", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/insilicoseq.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_insilicoseq/app/insilicoseq_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "ipyrad", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/ipyrad.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_ipyrad/app/ipyrad_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "ivar", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/ivar.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_ivar/app/ivar_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "jcvi", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/jcvi.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_jcvi/app/jcvi_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "kaiju", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/kaiju.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_kaiju/app/kaiju_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "khipu-metabolomics", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/khipu-metabolomics.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_khipu-metabolomics/app/khipu-metabolomics_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "kraken2", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/kraken2.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_kraken2/app/kraken2_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "liana", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/liana.manual_bundle.txt", + "run_help_command": false, + "status": "failed", + "error": "autogenerate_mcp_tool failed: Gemini request failed across all models. gemini-2.5-pro: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-3-flash-preview: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-2.5-flash: exception HTTPSConnectionPool(host='generativelanguage.googleapis.com', port=443): Read timed out. (read timeout=180)", + "output_file": "", + "dockerfile_ready": false + }, + { + "tool_name": "libsequence", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/libsequence.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_libsequence/app/libsequence_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "mageck", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/mageck.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_mageck/app/mageck_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "mash", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/mash.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_mash/app/mash_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "megahit", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/megahit.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_megahit/app/megahit_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "metaphlan", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/metaphlan.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_metaphlan/app/metaphlan_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "miniasm", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/miniasm.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_miniasm/app/miniasm_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "mlst", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/mlst.manual_bundle.txt", + "run_help_command": false, + "status": "failed", + "error": "autogenerate_mcp_tool failed: Gemini request failed across all models. gemini-2.5-pro: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-3-flash-preview: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-2.5-flash: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n", + "output_file": "", + "dockerfile_ready": false + }, + { + "tool_name": "moments", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/moments.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_moments/app/moments_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "msproteomicstools", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/msproteomicstools.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_msproteomicstools/app/msproteomicstools_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "multiqc-xenium-extra", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/multiqc-xenium-extra.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_multiqc-xenium-extra/app/multiqc-xenium-extra_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "mysql-connector-c", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/mysql-connector-c.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_mysql-connector-c/app/mysql-connector-c_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "nanomath", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/nanomath.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_nanomath/app/nanomath_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "ncbi-datasets-pylib", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/ncbi-datasets-pylib.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_ncbi-datasets-pylib/app/ncbi-datasets-pylib_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "ncls", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/ncls.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_ncls/app/ncls_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "nf-core", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/nf-core.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_nf-core/app/nf-core_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "ngs-tools", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/ngs-tools.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_ngs-tools/app/ngs-tools_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "openms-thirdparty", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/openms-thirdparty.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_openms-thirdparty/app/openms-thirdparty_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "pairix", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/pairix.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_pairix/app/pairix_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "pango-designation", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/pango-designation.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_pango-designation/app/pango-designation_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "parasail-python", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/parasail-python.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_parasail-python/app/parasail-python_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "pcdl", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/pcdl.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_pcdl/app/pcdl_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-algorithm-diff", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-algorithm-diff.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-algorithm-diff/app/perl-algorithm-diff_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-apache-test", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-apache-test.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-apache-test/app/perl-apache-test_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-array-compare", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-array-compare.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-array-compare/app/perl-array-compare_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-bio-coordinate", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-bio-coordinate.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-bio-coordinate/app/perl-bio-coordinate_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-bio-samtools", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-bio-samtools.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-bio-samtools/app/perl-bio-samtools_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-bio-tools-run-alignment-tcoffee", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-bio-tools-run-alignment-tcoffee.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-bio-tools-run-alignment-tcoffee/app/perl-bio-tools-run-alignment-tcoffee_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-business-isbn", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-business-isbn.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-business-isbn/app/perl-business-isbn_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-carp", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-carp.manual_bundle.txt", + "run_help_command": false, + "status": "failed", + "error": "refine failed (retry=1): Gemini request failed across all models. gemini-2.5-pro: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-3-flash-preview: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-2.5-flash: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n", + "output_file": "", + "dockerfile_ready": false + }, + { + "tool_name": "perl-class-load", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-class-load.manual_bundle.txt", + "run_help_command": false, + "status": "failed", + "error": "autogenerate_mcp_tool failed: Gemini request failed across all models. gemini-2.5-pro: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-3-flash-preview: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-2.5-flash: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n", + "output_file": "", + "dockerfile_ready": false + }, + { + "tool_name": "perl-common-sense", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-common-sense.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-common-sense/app/perl-common-sense_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-constant", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-constant.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-constant/app/perl-constant_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-cpan-meta-requirements", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-cpan-meta-requirements.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-cpan-meta-requirements/app/perl-cpan-meta-requirements_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-data-stag", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-data-stag.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-data-stag/app/perl-data-stag_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-dbd-sqlite", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-dbd-sqlite.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-dbd-sqlite/app/perl-dbd-sqlite_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-devel-stacktrace", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-devel-stacktrace.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-devel-stacktrace/app/perl-devel-stacktrace_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-digest-sha1", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-digest-sha1.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-digest-sha1/app/perl-digest-sha1_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-encode", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-encode.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-encode/app/perl-encode_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-exception-class", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-exception-class.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-exception-class/app/perl-exception-class_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-extutils-makemaker", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-extutils-makemaker.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-extutils-makemaker/app/perl-extutils-makemaker_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-file-listing", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-file-listing.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-file-listing/app/perl-file-listing_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-file-spec", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-file-spec.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-file-spec/app/perl-file-spec_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-font-ttf", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-font-ttf.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-font-ttf/app/perl-font-ttf_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-graphviz", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-graphviz.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-graphviz/app/perl-graphviz_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-html-parser", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-html-parser.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-html-parser/app/perl-html-parser_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-html-tree", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-html-tree.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-html-tree/app/perl-html-tree_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-http-date", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-http-date.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-http-date/app/perl-http-date_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-image-size", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-image-size.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-image-size/app/perl-image-size_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-io-sessiondata", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-io-sessiondata.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-io-sessiondata/app/perl-io-sessiondata_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-io-tty", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-io-tty.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-io-tty/app/perl-io-tty_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-ipc-sharelite", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-ipc-sharelite.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-ipc-sharelite/app/perl-ipc-sharelite_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-json-xs", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-json-xs.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-json-xs/app/perl-json-xs_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-list-moreutils", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-list-moreutils.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-list-moreutils/app/perl-list-moreutils_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-lwp-protocol-https", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-lwp-protocol-https.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-lwp-protocol-https/app/perl-lwp-protocol-https_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-math-cdf", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-math-cdf.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-math-cdf/app/perl-math-cdf_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-math-spline", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-math-spline.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-math-spline/app/perl-math-spline_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-mime-lite", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-mime-lite.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-mime-lite/app/perl-mime-lite_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-module-build", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-module-build.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-module-build/app/perl-module-build_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-module-load-conditional", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-module-load-conditional.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-module-load-conditional/app/perl-module-load-conditional_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-moo", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-moo.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-moo/app/perl-moo_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-net-http", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-net-http.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-net-http/app/perl-net-http_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-ole-storage_lite", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-ole-storage_lite.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-ole-storage_lite/app/perl-ole-storage_lite_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-parallel-forkmanager", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-parallel-forkmanager.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-parallel-forkmanager/app/perl-parallel-forkmanager_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-parent", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-parent.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-parent/app/perl-parent_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-pdf-api2", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-pdf-api2.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-pdf-api2/app/perl-pdf-api2_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-postscript", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-postscript.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-postscript/app/perl-postscript_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-scalar-list-utils", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-scalar-list-utils.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-scalar-list-utils/app/perl-scalar-list-utils_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-set-intervaltree", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-set-intervaltree.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-set-intervaltree/app/perl-set-intervaltree_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-socket", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-socket.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-socket/app/perl-socket_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-statistics-basic", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-statistics-basic.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-statistics-basic/app/perl-statistics-basic_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-sub-exporter-progressive", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-sub-exporter-progressive.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-sub-exporter-progressive/app/perl-sub-exporter-progressive_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-sub-quote", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-sub-quote.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-sub-quote/app/perl-sub-quote_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-task-weaken", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-task-weaken.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-task-weaken/app/perl-task-weaken_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-test-deep", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-test-deep.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-test-deep/app/perl-test-deep_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-test-harness", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-test-harness.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-test-harness/app/perl-test-harness_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-test-requiresinternet", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-test-requiresinternet.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-test-requiresinternet/app/perl-test-requiresinternet_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-text-diff", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-text-diff.manual_bundle.txt", + "run_help_command": false, + "status": "failed", + "error": "autogenerate_mcp_tool failed: Gemini request failed across all models. gemini-2.5-pro: HTTP 429 - {\n \"error\": {\n \"code\": 429,\n \"message\": \"You exceeded your current quota, please check your plan and billing details. For more information on this error, head to: https://ai.google.dev/gemini-api/docs/rate-limits. To monitor your current usage, head to: https://ai.dev/rate-limit. \\n* Quota exceeded for metric: generativelanguage.googleapis.com/generate_requests_per_model_per_day, limit: 1000, model: gemini-2.5-pro\\nPlease retry in 9h32m20.726872387s.\",\n \"status\": \"RESOURCE_EXHAUSTED\",\n \"details\": [\n {\n \"@type\": \"type.googleapis.com/google.rpc.Help\",\n \"links\": [\n {\n \"description\": \"Learn more about Gemini API quotas\",\n \"url\": \"https://ai.google.dev/gemini-api/docs/rate-limits\"\n }\n ]\n },\n {\n \"@type\": \"type.googleapis.com/google.rpc.QuotaFailure\",\n \"violations\": [\n {\n \"quotaMetric\": \"generativelanguage.googleapis.com/generate_requests_per_model_per_day\",\n \"quotaId\": \"GenerateRequestsPerDayPerProjectPerModel\",\n \"quotaDimensions\": {\n \"location\": \"global\",\n \"model\": \"gemini-2.5-pro\"\n },\n \"quotaValu | gemini-3-flash-preview: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-2.5-flash: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n", + "output_file": "", + "dockerfile_ready": false + }, + { + "tool_name": "perl-tie-ixhash", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-tie-ixhash.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-tie-ixhash/app/perl-tie-ixhash_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-tree-dag_node", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-tree-dag_node.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-tree-dag_node/app/perl-tree-dag_node_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-unicode-map", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-unicode-map.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-unicode-map/app/perl-unicode-map_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-www-robotrules", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-www-robotrules.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-www-robotrules/app/perl-www-robotrules_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-xml-libxml", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-xml-libxml.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-xml-libxml/app/perl-xml-libxml_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-xml-regexp", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-xml-regexp.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-xml-regexp/app/perl-xml-regexp_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-xml-sax-writer", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-xml-sax-writer.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-xml-sax-writer/app/perl-xml-sax-writer_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-xml-xpath", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-xml-xpath.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-xml-xpath/app/perl-xml-xpath_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "phyml", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/phyml.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_phyml/app/phyml_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "pilon", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/pilon.manual_bundle.txt", + "run_help_command": false, + "status": "failed", + "error": "autogenerate_mcp_tool failed: Gemini request failed across all models. gemini-2.5-pro: HTTP 429 - {\n \"error\": {\n \"code\": 429,\n \"message\": \"You exceeded your current quota, please check your plan and billing details. For more information on this error, head to: https://ai.google.dev/gemini-api/docs/rate-limits. To monitor your current usage, head to: https://ai.dev/rate-limit. \\n* Quota exceeded for metric: generativelanguage.googleapis.com/generate_requests_per_model_per_day, limit: 1000, model: gemini-2.5-pro\\nPlease retry in 9h23m11.860035304s.\",\n \"status\": \"RESOURCE_EXHAUSTED\",\n \"details\": [\n {\n \"@type\": \"type.googleapis.com/google.rpc.Help\",\n \"links\": [\n {\n \"description\": \"Learn more about Gemini API quotas\",\n \"url\": \"https://ai.google.dev/gemini-api/docs/rate-limits\"\n }\n ]\n },\n {\n \"@type\": \"type.googleapis.com/google.rpc.QuotaFailure\",\n \"violations\": [\n {\n \"quotaMetric\": \"generativelanguage.googleapis.com/generate_requests_per_model_per_day\",\n \"quotaId\": \"GenerateRequestsPerDayPerProjectPerModel\",\n \"quotaDimensions\": {\n \"model\": \"gemini-2.5-pro\",\n \"location\": \"global\"\n },\n \"quotaValu | gemini-3-flash-preview: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-2.5-flash: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n", + "output_file": "", + "dockerfile_ready": false + }, + { + "tool_name": "poa", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/poa.manual_bundle.txt", + "run_help_command": false, + "status": "failed", + "error": "autogenerate_mcp_tool failed: Gemini request failed across all models. gemini-2.5-pro: HTTP 429 - {\n \"error\": {\n \"code\": 429,\n \"message\": \"You exceeded your current quota, please check your plan and billing details. For more information on this error, head to: https://ai.google.dev/gemini-api/docs/rate-limits. To monitor your current usage, head to: https://ai.dev/rate-limit. \\n* Quota exceeded for metric: generativelanguage.googleapis.com/generate_requests_per_model_per_day, limit: 1000, model: gemini-2.5-pro\\nPlease retry in 9h23m10.543479243s.\",\n \"status\": \"RESOURCE_EXHAUSTED\",\n \"details\": [\n {\n \"@type\": \"type.googleapis.com/google.rpc.Help\",\n \"links\": [\n {\n \"description\": \"Learn more about Gemini API quotas\",\n \"url\": \"https://ai.google.dev/gemini-api/docs/rate-limits\"\n }\n ]\n },\n {\n \"@type\": \"type.googleapis.com/google.rpc.QuotaFailure\",\n \"violations\": [\n {\n \"quotaMetric\": \"generativelanguage.googleapis.com/generate_requests_per_model_per_day\",\n \"quotaId\": \"GenerateRequestsPerDayPerProjectPerModel\",\n \"quotaDimensions\": {\n \"model\": \"gemini-2.5-pro\",\n \"location\": \"global\"\n },\n \"quotaValu | gemini-3-flash-preview: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-2.5-flash: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n", + "output_file": "", + "dockerfile_ready": false + }, + { + "tool_name": "primer3", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/primer3.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_primer3/app/primer3_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "prophyle", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/prophyle.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_prophyle/app/prophyle_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "pubchempy", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/pubchempy.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_pubchempy/app/pubchempy_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "pybiolib", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/pybiolib.manual_bundle.txt", + "run_help_command": false, + "status": "failed", + "error": "autogenerate_mcp_tool failed: Gemini request failed across all models. gemini-2.5-pro: HTTP 429 - {\n \"error\": {\n \"code\": 429,\n \"message\": \"You exceeded your current quota, please check your plan and billing details. For more information on this error, head to: https://ai.google.dev/gemini-api/docs/rate-limits. To monitor your current usage, head to: https://ai.dev/rate-limit. \\n* Quota exceeded for metric: generativelanguage.googleapis.com/generate_requests_per_model_per_day, limit: 1000, model: gemini-2.5-pro\\nPlease retry in 9h21m45.688394101s.\",\n \"status\": \"RESOURCE_EXHAUSTED\",\n \"details\": [\n {\n \"@type\": \"type.googleapis.com/google.rpc.Help\",\n \"links\": [\n {\n \"description\": \"Learn more about Gemini API quotas\",\n \"url\": \"https://ai.google.dev/gemini-api/docs/rate-limits\"\n }\n ]\n },\n {\n \"@type\": \"type.googleapis.com/google.rpc.QuotaFailure\",\n \"violations\": [\n {\n \"quotaMetric\": \"generativelanguage.googleapis.com/generate_requests_per_model_per_day\",\n \"quotaId\": \"GenerateRequestsPerDayPerProjectPerModel\",\n \"quotaDimensions\": {\n \"location\": \"global\",\n \"model\": \"gemini-2.5-pro\"\n },\n \"quotaValu | gemini-3-flash-preview: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-2.5-flash: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n", + "output_file": "", + "dockerfile_ready": false + }, + { + "tool_name": "pyranges", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/pyranges.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_pyranges/app/pyranges_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "pysam", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/pysam.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_pysam/app/pysam_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "pyteomics", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/pyteomics.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_pyteomics/app/pyteomics_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "qcatch", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/qcatch.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_qcatch/app/qcatch_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "r-abdiv", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/r-abdiv.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_r-abdiv/app/r-abdiv_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "r-basejump", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/r-basejump.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_r-basejump/app/r-basejump_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "r-epitrace", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/r-epitrace.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_r-epitrace/app/r-epitrace_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "r-redeemr", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/r-redeemr.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_r-redeemr/app/r-redeemr_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "r-scopfunctions", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/r-scopfunctions.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_r-scopfunctions/app/r-scopfunctions_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "raxml", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/raxml.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_raxml/app/raxml_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "rnastructure", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/rnastructure.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_rnastructure/app/rnastructure_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "rscape", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/rscape.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_rscape/app/rscape_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "rust-bio-tools", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/rust-bio-tools.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_rust-bio-tools/app/rust-bio-tools_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "sambamba", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/sambamba.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_sambamba/app/sambamba_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "sc-musketeers", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/sc-musketeers.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_sc-musketeers/app/sc-musketeers_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "sccellfie", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/sccellfie.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_sccellfie/app/sccellfie_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "scirpy", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/scirpy.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_scirpy/app/scirpy_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "scstem", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/scstem.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_scstem/app/scstem_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "scvis", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/scvis.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_scvis/app/scvis_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "seacells", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/seacells.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_seacells/app/seacells_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "sepp", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/sepp.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_sepp/app/sepp_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "seqkit", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/seqkit.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_seqkit/app/seqkit_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "simo-omics", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/simo-omics.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_simo-omics/app/simo-omics_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "slow5tools", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/slow5tools.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_slow5tools/app/slow5tools_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "snakemake-interface-logger-plugins", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/snakemake-interface-logger-plugins.manual_bundle.txt", + "run_help_command": false, + "status": "failed", + "error": "autogenerate_mcp_tool failed: Gemini request failed across all models. gemini-2.5-pro: HTTP 429 - {\n \"error\": {\n \"code\": 429,\n \"message\": \"You exceeded your current quota, please check your plan and billing details. For more information on this error, head to: https://ai.google.dev/gemini-api/docs/rate-limits. To monitor your current usage, head to: https://ai.dev/rate-limit. \\n* Quota exceeded for metric: generativelanguage.googleapis.com/generate_requests_per_model_per_day, limit: 1000, model: gemini-2.5-pro\\nPlease retry in 9h6m24.322818481s.\",\n \"status\": \"RESOURCE_EXHAUSTED\",\n \"details\": [\n {\n \"@type\": \"type.googleapis.com/google.rpc.Help\",\n \"links\": [\n {\n \"description\": \"Learn more about Gemini API quotas\",\n \"url\": \"https://ai.google.dev/gemini-api/docs/rate-limits\"\n }\n ]\n },\n {\n \"@type\": \"type.googleapis.com/google.rpc.QuotaFailure\",\n \"violations\": [\n {\n \"quotaMetric\": \"generativelanguage.googleapis.com/generate_requests_per_model_per_day\",\n \"quotaId\": \"GenerateRequestsPerDayPerProjectPerModel\",\n \"quotaDimensions\": {\n \"model\": \"gemini-2.5-pro\",\n \"location\": \"global\"\n },\n \"quotaValue | gemini-3-flash-preview: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-2.5-flash: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n", + "output_file": "", + "dockerfile_ready": false + }, + { + "tool_name": "snakemake-wrapper-utils", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/snakemake-wrapper-utils.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_snakemake-wrapper-utils/app/snakemake-wrapper-utils_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "snpeff", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/snpeff.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_snpeff/app/snpeff_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "sortmerna", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/sortmerna.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_sortmerna/app/sortmerna_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "spapros", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/spapros.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_spapros/app/spapros_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "star", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/star.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_star/app/star_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "subread", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/subread.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_subread/app/subread_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "tabix", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/tabix.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_tabix/app/tabix_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "tb-profiler", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/tb-profiler.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_tb-profiler/app/tb-profiler_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "tmalign", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/tmalign.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_tmalign/app/tmalign_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "treetime", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/treetime.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_treetime/app/treetime_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "trimal", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/trimal.manual_bundle.txt", + "run_help_command": false, + "status": "failed", + "error": "autogenerate_mcp_tool failed: Gemini request failed across all models. gemini-2.5-pro: HTTP 429 - {\n \"error\": {\n \"code\": 429,\n \"message\": \"You exceeded your current quota, please check your plan and billing details. For more information on this error, head to: https://ai.google.dev/gemini-api/docs/rate-limits. To monitor your current usage, head to: https://ai.dev/rate-limit. \\n* Quota exceeded for metric: generativelanguage.googleapis.com/generate_requests_per_model_per_day, limit: 1000, model: gemini-2.5-pro\\nPlease retry in 9h1m31.140015018s.\",\n \"status\": \"RESOURCE_EXHAUSTED\",\n \"details\": [\n {\n \"@type\": \"type.googleapis.com/google.rpc.Help\",\n \"links\": [\n {\n \"description\": \"Learn more about Gemini API quotas\",\n \"url\": \"https://ai.google.dev/gemini-api/docs/rate-limits\"\n }\n ]\n },\n {\n \"@type\": \"type.googleapis.com/google.rpc.QuotaFailure\",\n \"violations\": [\n {\n \"quotaMetric\": \"generativelanguage.googleapis.com/generate_requests_per_model_per_day\",\n \"quotaId\": \"GenerateRequestsPerDayPerProjectPerModel\",\n \"quotaDimensions\": {\n \"location\": \"global\",\n \"model\": \"gemini-2.5-pro\"\n },\n \"quotaValue | gemini-3-flash-preview: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-2.5-flash: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n", + "output_file": "", + "dockerfile_ready": false + }, + { + "tool_name": "ucsc-bedgraphtobigwig", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/ucsc-bedgraphtobigwig.manual_bundle.txt", + "run_help_command": false, + "status": "failed", + "error": "autogenerate_mcp_tool failed: Gemini request failed across all models. gemini-2.5-pro: HTTP 429 - {\n \"error\": {\n \"code\": 429,\n \"message\": \"You exceeded your current quota, please check your plan and billing details. For more information on this error, head to: https://ai.google.dev/gemini-api/docs/rate-limits. To monitor your current usage, head to: https://ai.dev/rate-limit. \\n* Quota exceeded for metric: generativelanguage.googleapis.com/generate_requests_per_model_per_day, limit: 1000, model: gemini-2.5-pro\\nPlease retry in 9h1m30.200220377s.\",\n \"status\": \"RESOURCE_EXHAUSTED\",\n \"details\": [\n {\n \"@type\": \"type.googleapis.com/google.rpc.Help\",\n \"links\": [\n {\n \"description\": \"Learn more about Gemini API quotas\",\n \"url\": \"https://ai.google.dev/gemini-api/docs/rate-limits\"\n }\n ]\n },\n {\n \"@type\": \"type.googleapis.com/google.rpc.QuotaFailure\",\n \"violations\": [\n {\n \"quotaMetric\": \"generativelanguage.googleapis.com/generate_requests_per_model_per_day\",\n \"quotaId\": \"GenerateRequestsPerDayPerProjectPerModel\",\n \"quotaDimensions\": {\n \"location\": \"global\",\n \"model\": \"gemini-2.5-pro\"\n },\n \"quotaValue | gemini-3-flash-preview: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-2.5-flash: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n", + "output_file": "", + "dockerfile_ready": false + }, + { + "tool_name": "ucsc-gtftogenepred", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/ucsc-gtftogenepred.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_ucsc-gtftogenepred/app/ucsc-gtftogenepred_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "ucsc-twobittofa", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/ucsc-twobittofa.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_ucsc-twobittofa/app/ucsc-twobittofa_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "unicycler", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/unicycler.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_unicycler/app/unicycler_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "urllib3", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/urllib3.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_urllib3/app/urllib3_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "varlociraptor", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/varlociraptor.manual_bundle.txt", + "run_help_command": false, + "status": "failed", + "error": "autogenerate_mcp_tool failed: Gemini request failed across all models. gemini-2.5-pro: HTTP 429 - {\n \"error\": {\n \"code\": 429,\n \"message\": \"You exceeded your current quota, please check your plan and billing details. For more information on this error, head to: https://ai.google.dev/gemini-api/docs/rate-limits. To monitor your current usage, head to: https://ai.dev/rate-limit. \\n* Quota exceeded for metric: generativelanguage.googleapis.com/generate_requests_per_model_per_day, limit: 1000, model: gemini-2.5-pro\\nPlease retry in 8h59m1.993030901s.\",\n \"status\": \"RESOURCE_EXHAUSTED\",\n \"details\": [\n {\n \"@type\": \"type.googleapis.com/google.rpc.Help\",\n \"links\": [\n {\n \"description\": \"Learn more about Gemini API quotas\",\n \"url\": \"https://ai.google.dev/gemini-api/docs/rate-limits\"\n }\n ]\n },\n {\n \"@type\": \"type.googleapis.com/google.rpc.QuotaFailure\",\n \"violations\": [\n {\n \"quotaMetric\": \"generativelanguage.googleapis.com/generate_requests_per_model_per_day\",\n \"quotaId\": \"GenerateRequestsPerDayPerProjectPerModel\",\n \"quotaDimensions\": {\n \"location\": \"global\",\n \"model\": \"gemini-2.5-pro\"\n },\n \"quotaValue | gemini-3-flash-preview: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-2.5-flash: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n", + "output_file": "", + "dockerfile_ready": false + }, + { + "tool_name": "vcftools", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/vcftools.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_vcftools/app/vcftools_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "vpt", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/vpt.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_vpt/app/vpt_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "whatshap", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/whatshap.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_whatshap/app/whatshap_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "yacrd", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/yacrd.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_yacrd/app/yacrd_server.py", + "dockerfile_ready": true + } +] \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/conversion_report.shard_2_of_4.json b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/conversion_report.shard_2_of_4.json new file mode 100644 index 0000000000000000000000000000000000000000..8aaae94dc2029bcee885707bc7f29732f75e804b --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/conversion_report.shard_2_of_4.json @@ -0,0 +1,2108 @@ +[ + { + "tool_name": "abundancebin", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/abundancebin.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_abundancebin/app/abundancebin_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "anarci", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/anarci.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_anarci/app/anarci_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "arriba", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/arriba.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_arriba/app/arriba_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "atropos", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/atropos.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_atropos/app/atropos_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "barrnap", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/barrnap.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_barrnap/app/barrnap_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bin2cell", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bin2cell.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bin2cell/app/bin2cell_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconda-repodata-patches", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconda-repodata-patches.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconda-repodata-patches/app/bioconda-repodata-patches_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-alabaster", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-alabaster.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-alabaster/app/bioconductor-alabaster_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-annotationfilter", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-annotationfilter.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-annotationfilter/app/bioconductor-annotationfilter_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-banksy", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-banksy.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-banksy/app/bioconductor-banksy_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-biocio", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-biocio.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-biocio/app/bioconductor-biocio_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-bluster", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-bluster.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-bluster/app/bioconductor-bluster_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-catscradle", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-catscradle.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-catscradle/app/bioconductor-catscradle_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-cellmigration", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-cellmigration.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-cellmigration/app/bioconductor-cellmigration_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-complexheatmap", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-complexheatmap.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-complexheatmap/app/bioconductor-complexheatmap_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-ctsv", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-ctsv.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-ctsv/app/bioconductor-ctsv_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-decipher", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-decipher.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-decipher/app/bioconductor-decipher_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-delayedarray", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-delayedarray.manual_bundle.txt", + "run_help_command": false, + "status": "failed", + "error": "autogenerate_mcp_tool failed: Gemini request failed across all models. gemini-2.5-pro: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-3-flash-preview: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-2.5-flash: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n", + "output_file": "", + "dockerfile_ready": false + }, + { + "tool_name": "bioconductor-diffbind", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-diffbind.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-diffbind/app/bioconductor-diffbind_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-dose", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-dose.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-dose/app/bioconductor-dose_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-erma", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-erma.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-erma/app/bioconductor-erma_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-genefilter", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-genefilter.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-genefilter/app/bioconductor-genefilter_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-genomicalignments", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-genomicalignments.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-genomicalignments/app/bioconductor-genomicalignments_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-ggsc", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-ggsc.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-ggsc/app/bioconductor-ggsc_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-go.db", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-go.db.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-go.db/app/bioconductor-go.db_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-hdf5array", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-hdf5array.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-hdf5array/app/bioconductor-hdf5array_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-imcrtools", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-imcrtools.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-imcrtools/app/bioconductor-imcrtools_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-irisfgm", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-irisfgm.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-irisfgm/app/bioconductor-irisfgm_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-lisaclust", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-lisaclust.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-lisaclust/app/bioconductor-lisaclust_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-metapod", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-metapod.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-metapod/app/bioconductor-metapod_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-mspurity", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-mspurity.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-mspurity/app/bioconductor-mspurity_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-nnsvg", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-nnsvg.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-nnsvg/app/bioconductor-nnsvg_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-org.hs.eg.db", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-org.hs.eg.db.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-org.hs.eg.db/app/bioconductor-org.hs.eg.db_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-pipecomp", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-pipecomp.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-pipecomp/app/bioconductor-pipecomp_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-qvalue", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-qvalue.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-qvalue/app/bioconductor-qvalue_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-rgraphviz", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-rgraphviz.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-rgraphviz/app/bioconductor-rgraphviz_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-rhtslib", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-rhtslib.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-rhtslib/app/bioconductor-rhtslib_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-s4arrays", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-s4arrays.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-s4arrays/app/bioconductor-s4arrays_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-scannotatr", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-scannotatr.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-scannotatr/app/bioconductor-scannotatr_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-scclassifr", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-scclassifr.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-scclassifr/app/bioconductor-scclassifr_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-scqtltools", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-scqtltools.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-scqtltools/app/bioconductor-scqtltools_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-scuttle", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-scuttle.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-scuttle/app/bioconductor-scuttle_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-signifinder", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-signifinder.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-signifinder/app/bioconductor-signifinder_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-smoppix", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-smoppix.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-smoppix/app/bioconductor-smoppix_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-spanorm", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-spanorm.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-spanorm/app/bioconductor-spanorm_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-spasim", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-spasim.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-spasim/app/bioconductor-spasim_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-spatialexperimentio", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-spatialexperimentio.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-spatialexperimentio/app/bioconductor-spatialexperimentio_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-spatialomicsoverlay", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-spatialomicsoverlay.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-spatialomicsoverlay/app/bioconductor-spatialomicsoverlay_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-spicyr", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-spicyr.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-spicyr/app/bioconductor-spicyr_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-standr", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-standr.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-standr/app/bioconductor-standr_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-stpipe", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-stpipe.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-stpipe/app/bioconductor-stpipe_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-synergyfinder", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-synergyfinder.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-synergyfinder/app/bioconductor-synergyfinder_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-tidyspatialexperiment", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-tidyspatialexperiment.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-tidyspatialexperiment/app/bioconductor-tidyspatialexperiment_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-tximport", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-tximport.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-tximport/app/bioconductor-tximport_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-visiumstitched", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-visiumstitched.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-visiumstitched/app/bioconductor-visiumstitched_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-xeniumio", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-xeniumio.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-xeniumio/app/bioconductor-xeniumio_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "biom-format", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/biom-format.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_biom-format/app/biom-format_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bowtie", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bowtie.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bowtie/app/bowtie_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bwa", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bwa.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bwa/app/bwa_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "cd-hit", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/cd-hit.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_cd-hit/app/cd-hit_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "cellqc", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/cellqc.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_cellqc/app/cellqc_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "checkm-genome", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/checkm-genome.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_checkm-genome/app/checkm-genome_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "clustalw", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/clustalw.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_clustalw/app/clustalw_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "comet-ms", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/comet-ms.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_comet-ms/app/comet-ms_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "coreutils", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/coreutils.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_coreutils/app/coreutils_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "crispritz", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/crispritz.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_crispritz/app/crispritz_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "cutadapt", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/cutadapt.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_cutadapt/app/cutadapt_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "dca", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/dca.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_dca/app/dca_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "deeptoolsintervals", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/deeptoolsintervals.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_deeptoolsintervals/app/deeptoolsintervals_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "dnaio", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/dnaio.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_dnaio/app/dnaio_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "dxpy", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/dxpy.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_dxpy/app/dxpy_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "ena-webin-cli", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/ena-webin-cli.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_ena-webin-cli/app/ena-webin-cli_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "episcanpy", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/episcanpy.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_episcanpy/app/episcanpy_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "fast5", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/fast5.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_fast5/app/fast5_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "fastqc", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/fastqc.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_fastqc/app/fastqc_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "filechunkio", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/filechunkio.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_filechunkio/app/filechunkio_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "freebayes", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/freebayes.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_freebayes/app/freebayes_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "gatk", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/gatk.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_gatk/app/gatk_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "gecode", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/gecode.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_gecode/app/gecode_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "geneimpacts", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/geneimpacts.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_geneimpacts/app/geneimpacts_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "genomepy", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/genomepy.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_genomepy/app/genomepy_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "ghostscript", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/ghostscript.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_ghostscript/app/ghostscript_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "gneiss", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/gneiss.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_gneiss/app/gneiss_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "gridss", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/gridss.manual_bundle.txt", + "run_help_command": false, + "status": "failed", + "error": "refine failed (retry=1): Gemini request failed across all models. gemini-2.5-pro: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-3-flash-preview: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-2.5-flash: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n", + "output_file": "", + "dockerfile_ready": false + }, + { + "tool_name": "harpy", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/harpy.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_harpy/app/harpy_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "hisat2", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/hisat2.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_hisat2/app/hisat2_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "humann", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/humann.manual_bundle.txt", + "run_help_command": false, + "status": "failed", + "error": "autogenerate_mcp_tool failed: Gemini request failed across all models. gemini-2.5-pro: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-3-flash-preview: exception HTTPSConnectionPool(host='generativelanguage.googleapis.com', port=443): Read timed out. (read timeout=180) | gemini-2.5-flash: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n", + "output_file": "", + "dockerfile_ready": false + }, + { + "tool_name": "igv-reports", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/igv-reports.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_igv-reports/app/igv-reports_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "intarna", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/intarna.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_intarna/app/intarna_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "ipython-cluster-helper", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/ipython-cluster-helper.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_ipython-cluster-helper/app/ipython-cluster-helper_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "jalview", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/jalview.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_jalview/app/jalview_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "jellyfish", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/jellyfish.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_jellyfish/app/jellyfish_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "kalign2", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/kalign2.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_kalign2/app/kalign2_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "kma", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/kma.manual_bundle.txt", + "run_help_command": false, + "status": "failed", + "error": "autogenerate_mcp_tool failed: Gemini request failed across all models. gemini-2.5-pro: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-3-flash-preview: exception HTTPSConnectionPool(host='generativelanguage.googleapis.com', port=443): Read timed out. (read timeout=180) | gemini-2.5-flash: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n", + "output_file": "", + "dockerfile_ready": false + }, + { + "tool_name": "krona", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/krona.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_krona/app/krona_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "libcifpp", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/libcifpp.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_libcifpp/app/libcifpp_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "locarna", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/locarna.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_locarna/app/locarna_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "mameshiba", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/mameshiba.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_mameshiba/app/mameshiba_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "maxquant", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/maxquant.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_maxquant/app/maxquant_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "meme", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/meme.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_meme/app/meme_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "metaphlan2", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/metaphlan2.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_metaphlan2/app/metaphlan2_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "minimap2", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/minimap2.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_minimap2/app/minimap2_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "mmseqs2", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/mmseqs2.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_mmseqs2/app/mmseqs2_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "mosdepth", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/mosdepth.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_mosdepth/app/mosdepth_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "msstitch", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/msstitch.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_msstitch/app/msstitch_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "mummer", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/mummer.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_mummer/app/mummer_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "mztosqlite", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/mztosqlite.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_mztosqlite/app/mztosqlite_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "nanoplot", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/nanoplot.manual_bundle.txt", + "run_help_command": false, + "status": "failed", + "error": "autogenerate_mcp_tool failed: Gemini request failed across all models. gemini-2.5-pro: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-3-flash-preview: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-2.5-flash: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n", + "output_file": "", + "dockerfile_ready": false + }, + { + "tool_name": "ncbi-genome-download", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/ncbi-genome-download.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_ncbi-genome-download/app/ncbi-genome-download_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "nextalign", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/nextalign.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_nextalign/app/nextalign_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "nglview", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/nglview.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_nglview/app/nglview_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "novae", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/novae.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_novae/app/novae_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "opticlust", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/opticlust.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_opticlust/app/opticlust_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "palantir", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/palantir.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_palantir/app/palantir_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "pangolearn", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/pangolearn.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_pangolearn/app/pangolearn_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "pasta", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/pasta.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_pasta/app/pasta_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "peakqc", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/peakqc.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_peakqc/app/peakqc_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-algorithm-munkres", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-algorithm-munkres.manual_bundle.txt", + "run_help_command": false, + "status": "failed", + "error": "autogenerate_mcp_tool failed: Gemini request failed across all models. gemini-2.5-pro: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-3-flash-preview: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-2.5-flash: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n", + "output_file": "", + "dockerfile_ready": false + }, + { + "tool_name": "perl-app-cpanminus", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-app-cpanminus.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-app-cpanminus/app/perl-app-cpanminus_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-autoloader", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-autoloader.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-autoloader/app/perl-autoloader_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-bio-db-hts", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-bio-db-hts.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-bio-db-hts/app/perl-bio-db-hts_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-bio-searchio-hmmer", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-bio-searchio-hmmer.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-bio-searchio-hmmer/app/perl-bio-searchio-hmmer_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-bioperl", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-bioperl.manual_bundle.txt", + "run_help_command": false, + "status": "failed", + "error": "autogenerate_mcp_tool failed: Gemini request failed across all models. gemini-2.5-pro: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-3-flash-preview: exception HTTPSConnectionPool(host='generativelanguage.googleapis.com', port=443): Read timed out. (read timeout=180) | gemini-2.5-flash: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n", + "output_file": "", + "dockerfile_ready": false + }, + { + "tool_name": "perl-business-isbn-data", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-business-isbn-data.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-business-isbn-data/app/perl-business-isbn-data_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-cgi", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-cgi.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-cgi/app/perl-cgi_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-class-load-xs", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-class-load-xs.manual_bundle.txt", + "run_help_command": false, + "status": "failed", + "error": "autogenerate_mcp_tool failed: Gemini request failed across all models. gemini-2.5-pro: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-3-flash-preview: exception HTTPSConnectionPool(host='generativelanguage.googleapis.com', port=443): Read timed out. (read timeout=180) | gemini-2.5-flash: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n", + "output_file": "", + "dockerfile_ready": false + }, + { + "tool_name": "perl-compress-raw-bzip2", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-compress-raw-bzip2.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-compress-raw-bzip2/app/perl-compress-raw-bzip2_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-convert-binary-c", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-convert-binary-c.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-convert-binary-c/app/perl-convert-binary-c_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-crypt-rc4", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-crypt-rc4.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-crypt-rc4/app/perl-crypt-rc4_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-date-format", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-date-format.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-date-format/app/perl-date-format_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-dbi", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-dbi.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-dbi/app/perl-dbi_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-digest-hmac", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-digest-hmac.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-digest-hmac/app/perl-digest-hmac_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-dist-checkconflicts", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-dist-checkconflicts.manual_bundle.txt", + "run_help_command": false, + "status": "failed", + "error": "autogenerate_mcp_tool failed: Gemini request failed across all models. gemini-2.5-pro: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-3-flash-preview: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-2.5-flash: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n", + "output_file": "", + "dockerfile_ready": false + }, + { + "tool_name": "perl-encode-locale", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-encode-locale.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-encode-locale/app/perl-encode-locale_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-exporter", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-exporter.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-exporter/app/perl-exporter_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-extutils-manifest", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-extutils-manifest.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-extutils-manifest/app/perl-extutils-manifest_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-file-path", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-file-path.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-file-path/app/perl-file-path_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-file-temp", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-file-temp.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-file-temp/app/perl-file-temp_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-gd", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-gd.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-gd/app/perl-gd_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-html-element-extended", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-html-element-extended.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-html-element-extended/app/perl-html-element-extended_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-html-tableextract", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-html-tableextract.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-html-tableextract/app/perl-html-tableextract_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-html-treebuilder-xpath", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-html-treebuilder-xpath.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-html-treebuilder-xpath/app/perl-html-treebuilder-xpath_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-http-message", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-http-message.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-http-message/app/perl-http-message_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-importer", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-importer.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-importer/app/perl-importer_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-io-socket-ssl", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-io-socket-ssl.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-io-socket-ssl/app/perl-io-socket-ssl_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-io-zlib", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-io-zlib.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-io-zlib/app/perl-io-zlib_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-jcode", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-jcode.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-jcode/app/perl-jcode_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-lib", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-lib.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-lib/app/perl-lib_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-list-moreutils-xs", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-list-moreutils-xs.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-list-moreutils-xs/app/perl-list-moreutils-xs_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-lwp-simple", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-lwp-simple.manual_bundle.txt", + "run_help_command": false, + "status": "failed", + "error": "autogenerate_mcp_tool failed: Gemini request failed across all models. gemini-2.5-pro: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-3-flash-preview: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-2.5-flash: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n", + "output_file": "", + "dockerfile_ready": false + }, + { + "tool_name": "perl-math-derivative", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-math-derivative.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-math-derivative/app/perl-math-derivative_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-math-vecstat", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-math-vecstat.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-math-vecstat/app/perl-math-vecstat_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-mime-tools", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-mime-tools.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-mime-tools/app/perl-mime-tools_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-module-corelist", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-module-corelist.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-module-corelist/app/perl-module-corelist_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-module-metadata", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-module-metadata.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-module-metadata/app/perl-module-metadata_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-moose", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-moose.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-moose/app/perl-moose_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-net-ssleay", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-net-ssleay.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-net-ssleay/app/perl-net-ssleay_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-package-deprecationmanager", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-package-deprecationmanager.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-package-deprecationmanager/app/perl-package-deprecationmanager_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-params-check", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-params-check.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-params-check/app/perl-params-check_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-parse-recdescent", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-parse-recdescent.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-parse-recdescent/app/perl-parse-recdescent_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-perlio-gzip", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-perlio-gzip.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-perlio-gzip/app/perl-perlio-gzip_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-readonly", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-readonly.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-readonly/app/perl-readonly_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-sereal", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-sereal.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-sereal/app/perl-sereal_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-set-intspan", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-set-intspan.manual_bundle.txt", + "run_help_command": false, + "status": "failed", + "error": "autogenerate_mcp_tool failed: Gemini request failed across all models. gemini-2.5-pro: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-3-flash-preview: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-2.5-flash: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n", + "output_file": "", + "dockerfile_ready": false + }, + { + "tool_name": "perl-sort-naturally", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-sort-naturally.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-sort-naturally/app/perl-sort-naturally_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-statistics-descriptive", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-statistics-descriptive.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-statistics-descriptive/app/perl-statistics-descriptive_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-sub-identify", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-sub-identify.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-sub-identify/app/perl-sub-identify_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-sub-uplevel", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-sub-uplevel.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-sub-uplevel/app/perl-sub-uplevel_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-template-toolkit", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-template-toolkit.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-template-toolkit/app/perl-template-toolkit_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-test-differences", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-test-differences.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-test-differences/app/perl-test-differences_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-test-leaktrace", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-test-leaktrace.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-test-leaktrace/app/perl-test-leaktrace_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-test-warn", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-test-warn.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-test-warn/app/perl-test-warn_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-text-format", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-text-format.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-text-format/app/perl-text-format_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-time-hires", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-time-hires.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-time-hires/app/perl-time-hires_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-try-tiny", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-try-tiny.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-try-tiny/app/perl-try-tiny_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-uri", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-uri.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-uri/app/perl-uri_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-xml-dom", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-xml-dom.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-xml-dom/app/perl-xml-dom_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-xml-libxslt", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-xml-libxslt.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-xml-libxslt/app/perl-xml-libxslt_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-xml-sax", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-xml-sax.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-xml-sax/app/perl-xml-sax_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-xml-simple", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-xml-simple.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-xml-simple/app/perl-xml-simple_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-xml-xpathengine", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-xml-xpathengine.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-xml-xpathengine/app/perl-xml-xpathengine_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "piaso", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/piaso.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_piaso/app/piaso_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "pixelator", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/pixelator.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_pixelator/app/pixelator_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "portcullis", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/portcullis.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_portcullis/app/portcullis_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "primer3-py", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/primer3-py.manual_bundle.txt", + "run_help_command": false, + "status": "failed", + "error": "autogenerate_mcp_tool failed: Gemini request failed across all models. gemini-2.5-pro: HTTP 429 - {\n \"error\": {\n \"code\": 429,\n \"message\": \"You exceeded your current quota, please check your plan and billing details. For more information on this error, head to: https://ai.google.dev/gemini-api/docs/rate-limits. To monitor your current usage, head to: https://ai.dev/rate-limit. \\n* Quota exceeded for metric: generativelanguage.googleapis.com/generate_requests_per_model_per_day, limit: 1000, model: gemini-2.5-pro\\nPlease retry in 9h38m15.21563304s.\",\n \"status\": \"RESOURCE_EXHAUSTED\",\n \"details\": [\n {\n \"@type\": \"type.googleapis.com/google.rpc.Help\",\n \"links\": [\n {\n \"description\": \"Learn more about Gemini API quotas\",\n \"url\": \"https://ai.google.dev/gemini-api/docs/rate-limits\"\n }\n ]\n },\n {\n \"@type\": \"type.googleapis.com/google.rpc.QuotaFailure\",\n \"violations\": [\n {\n \"quotaMetric\": \"generativelanguage.googleapis.com/generate_requests_per_model_per_day\",\n \"quotaId\": \"GenerateRequestsPerDayPerProjectPerModel\",\n \"quotaDimensions\": {\n \"location\": \"global\",\n \"model\": \"gemini-2.5-pro\"\n },\n \"quotaValue | gemini-3-flash-preview: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-2.5-flash: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n", + "output_file": "", + "dockerfile_ready": false + }, + { + "tool_name": "proteinortho", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/proteinortho.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_proteinortho/app/proteinortho_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "py2bit", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/py2bit.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_py2bit/app/py2bit_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "pyfaidx", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/pyfaidx.manual_bundle.txt", + "run_help_command": false, + "status": "failed", + "error": "autogenerate_mcp_tool failed: Gemini request failed across all models. gemini-2.5-pro: HTTP 429 - {\n \"error\": {\n \"code\": 429,\n \"message\": \"You exceeded your current quota, please check your plan and billing details. For more information on this error, head to: https://ai.google.dev/gemini-api/docs/rate-limits. To monitor your current usage, head to: https://ai.dev/rate-limit. \\n* Quota exceeded for metric: generativelanguage.googleapis.com/generate_requests_per_model_per_day, limit: 1000, model: gemini-2.5-pro\\nPlease retry in 9h37m23.552212461s.\",\n \"status\": \"RESOURCE_EXHAUSTED\",\n \"details\": [\n {\n \"@type\": \"type.googleapis.com/google.rpc.Help\",\n \"links\": [\n {\n \"description\": \"Learn more about Gemini API quotas\",\n \"url\": \"https://ai.google.dev/gemini-api/docs/rate-limits\"\n }\n ]\n },\n {\n \"@type\": \"type.googleapis.com/google.rpc.QuotaFailure\",\n \"violations\": [\n {\n \"quotaMetric\": \"generativelanguage.googleapis.com/generate_requests_per_model_per_day\",\n \"quotaId\": \"GenerateRequestsPerDayPerProjectPerModel\",\n \"quotaDimensions\": {\n \"location\": \"global\",\n \"model\": \"gemini-2.5-pro\"\n },\n \"quotaValu | gemini-3-flash-preview: exception HTTPSConnectionPool(host='generativelanguage.googleapis.com', port=443): Read timed out. (read timeout=180) | gemini-2.5-flash: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n", + "output_file": "", + "dockerfile_ready": false + }, + { + "tool_name": "pyrle", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/pyrle.manual_bundle.txt", + "run_help_command": false, + "status": "failed", + "error": "autogenerate_mcp_tool failed: Gemini request failed across all models. gemini-2.5-pro: HTTP 429 - {\n \"error\": {\n \"code\": 429,\n \"message\": \"You exceeded your current quota, please check your plan and billing details. For more information on this error, head to: https://ai.google.dev/gemini-api/docs/rate-limits. To monitor your current usage, head to: https://ai.dev/rate-limit. \\n* Quota exceeded for metric: generativelanguage.googleapis.com/generate_requests_per_model_per_day, limit: 1000, model: gemini-2.5-pro\\nPlease retry in 9h34m22.492452284s.\",\n \"status\": \"RESOURCE_EXHAUSTED\",\n \"details\": [\n {\n \"@type\": \"type.googleapis.com/google.rpc.Help\",\n \"links\": [\n {\n \"description\": \"Learn more about Gemini API quotas\",\n \"url\": \"https://ai.google.dev/gemini-api/docs/rate-limits\"\n }\n ]\n },\n {\n \"@type\": \"type.googleapis.com/google.rpc.QuotaFailure\",\n \"violations\": [\n {\n \"quotaMetric\": \"generativelanguage.googleapis.com/generate_requests_per_model_per_day\",\n \"quotaId\": \"GenerateRequestsPerDayPerProjectPerModel\",\n \"quotaDimensions\": {\n \"model\": \"gemini-2.5-pro\",\n \"location\": \"global\"\n },\n \"quotaValu | gemini-3-flash-preview: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-2.5-flash: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n", + "output_file": "", + "dockerfile_ready": false + }, + { + "tool_name": "pysftp", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/pysftp.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_pysftp/app/pysftp_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "python-edlib", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/python-edlib.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_python-edlib/app/python-edlib_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "qiime", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/qiime.manual_bundle.txt", + "run_help_command": false, + "status": "failed", + "error": "regenerate failed (retry=1): Gemini request failed across all models. gemini-2.5-pro: HTTP 429 - {\n \"error\": {\n \"code\": 429,\n \"message\": \"You exceeded your current quota, please check your plan and billing details. For more information on this error, head to: https://ai.google.dev/gemini-api/docs/rate-limits. To monitor your current usage, head to: https://ai.dev/rate-limit. \\n* Quota exceeded for metric: generativelanguage.googleapis.com/generate_requests_per_model_per_day, limit: 1000, model: gemini-2.5-pro\\nPlease retry in 9h32m46.544993021s.\",\n \"status\": \"RESOURCE_EXHAUSTED\",\n \"details\": [\n {\n \"@type\": \"type.googleapis.com/google.rpc.Help\",\n \"links\": [\n {\n \"description\": \"Learn more about Gemini API quotas\",\n \"url\": \"https://ai.google.dev/gemini-api/docs/rate-limits\"\n }\n ]\n },\n {\n \"@type\": \"type.googleapis.com/google.rpc.QuotaFailure\",\n \"violations\": [\n {\n \"quotaMetric\": \"generativelanguage.googleapis.com/generate_requests_per_model_per_day\",\n \"quotaId\": \"GenerateRequestsPerDayPerProjectPerModel\",\n \"quotaDimensions\": {\n \"model\": \"gemini-2.5-pro\",\n \"location\": \"global\"\n },\n \"quotaValu | gemini-3-flash-preview: exception HTTPSConnectionPool(host='generativelanguage.googleapis.com', port=443): Read timed out. (read timeout=180) | gemini-2.5-flash: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n", + "output_file": "", + "dockerfile_ready": false + }, + { + "tool_name": "r-acidbase", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/r-acidbase.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_r-acidbase/app/r-acidbase_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "r-beyondcell", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/r-beyondcell.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_r-beyondcell/app/r-beyondcell_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "r-flanders", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/r-flanders.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_r-flanders/app/r-flanders_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "r-restfulr", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/r-restfulr.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_r-restfulr/app/r-restfulr_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "r-seurat-data", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/r-seurat-data.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_r-seurat-data/app/r-seurat-data_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "recognizer", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/recognizer.manual_bundle.txt", + "run_help_command": false, + "status": "failed", + "error": "autogenerate_mcp_tool failed: Gemini request failed across all models. gemini-2.5-pro: HTTP 429 - {\n \"error\": {\n \"code\": 429,\n \"message\": \"You exceeded your current quota, please check your plan and billing details. For more information on this error, head to: https://ai.google.dev/gemini-api/docs/rate-limits. To monitor your current usage, head to: https://ai.dev/rate-limit. \\n* Quota exceeded for metric: generativelanguage.googleapis.com/generate_requests_per_model_per_day, limit: 1000, model: gemini-2.5-pro\\nPlease retry in 9h28m26.883569646s.\",\n \"status\": \"RESOURCE_EXHAUSTED\",\n \"details\": [\n {\n \"@type\": \"type.googleapis.com/google.rpc.Help\",\n \"links\": [\n {\n \"description\": \"Learn more about Gemini API quotas\",\n \"url\": \"https://ai.google.dev/gemini-api/docs/rate-limits\"\n }\n ]\n },\n {\n \"@type\": \"type.googleapis.com/google.rpc.QuotaFailure\",\n \"violations\": [\n {\n \"quotaMetric\": \"generativelanguage.googleapis.com/generate_requests_per_model_per_day\",\n \"quotaId\": \"GenerateRequestsPerDayPerProjectPerModel\",\n \"quotaDimensions\": {\n \"location\": \"global\",\n \"model\": \"gemini-2.5-pro\"\n },\n \"quotaValu | gemini-3-flash-preview: exception HTTPSConnectionPool(host='generativelanguage.googleapis.com', port=443): Read timed out. (read timeout=180) | gemini-2.5-flash: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n", + "output_file": "", + "dockerfile_ready": false + }, + { + "tool_name": "rnftools", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/rnftools.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_rnftools/app/rnftools_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "rsem", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/rsem.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_rsem/app/rsem_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "sage-proteomics", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/sage-proteomics.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_sage-proteomics/app/sage-proteomics_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "samblaster", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/samblaster.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_samblaster/app/samblaster_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "scaden", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/scaden.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_scaden/app/scaden_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "scepia", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/scepia.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_scepia/app/scepia_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "scmidas", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/scmidas.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_scmidas/app/scmidas_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "sctriangulate", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/sctriangulate.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_sctriangulate/app/sctriangulate_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "scvis_galaxy", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/scvis_galaxy.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_scvis_galaxy/app/scvis_galaxy_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "searchgui", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/searchgui.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_searchgui/app/searchgui_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "seq2science", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/seq2science.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_seq2science/app/seq2science_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "seqtk", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/seqtk.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_seqtk/app/seqtk_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "simplejson", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/simplejson.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_simplejson/app/simplejson_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "snakemake", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/snakemake.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_snakemake/app/snakemake_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "snakemake-interface-report-plugins", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/snakemake-interface-report-plugins.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_snakemake-interface-report-plugins/app/snakemake-interface-report-plugins_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "snakesv", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/snakesv.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_snakesv/app/snakesv_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "snpsift", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/snpsift.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_snpsift/app/snpsift_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "sourmash", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/sourmash.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_sourmash/app/sourmash_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "spatialleiden", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/spatialleiden.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_spatialleiden/app/spatialleiden_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "star-fusion", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/star-fusion.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_star-fusion/app/star-fusion_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "switchtfi", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/switchtfi.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_switchtfi/app/switchtfi_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "tabixpp", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/tabixpp.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_tabixpp/app/tabixpp_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "thapbi-pict", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/thapbi-pict.manual_bundle.txt", + "run_help_command": false, + "status": "failed", + "error": "autogenerate_mcp_tool failed: Gemini request failed across all models. gemini-2.5-pro: HTTP 429 - {\n \"error\": {\n \"code\": 429,\n \"message\": \"You exceeded your current quota, please check your plan and billing details. For more information on this error, head to: https://ai.google.dev/gemini-api/docs/rate-limits. To monitor your current usage, head to: https://ai.dev/rate-limit. \\n* Quota exceeded for metric: generativelanguage.googleapis.com/generate_requests_per_model_per_day, limit: 1000, model: gemini-2.5-pro\\nPlease retry in 9h7m25.710777538s.\",\n \"status\": \"RESOURCE_EXHAUSTED\",\n \"details\": [\n {\n \"@type\": \"type.googleapis.com/google.rpc.Help\",\n \"links\": [\n {\n \"description\": \"Learn more about Gemini API quotas\",\n \"url\": \"https://ai.google.dev/gemini-api/docs/rate-limits\"\n }\n ]\n },\n {\n \"@type\": \"type.googleapis.com/google.rpc.QuotaFailure\",\n \"violations\": [\n {\n \"quotaMetric\": \"generativelanguage.googleapis.com/generate_requests_per_model_per_day\",\n \"quotaId\": \"GenerateRequestsPerDayPerProjectPerModel\",\n \"quotaDimensions\": {\n \"location\": \"global\",\n \"model\": \"gemini-2.5-pro\"\n },\n \"quotaValue | gemini-3-flash-preview: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-2.5-flash: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n", + "output_file": "", + "dockerfile_ready": false + }, + { + "tool_name": "tobias", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/tobias.manual_bundle.txt", + "run_help_command": false, + "status": "failed", + "error": "autogenerate_mcp_tool failed: Gemini request failed across all models. gemini-2.5-pro: HTTP 429 - {\n \"error\": {\n \"code\": 429,\n \"message\": \"You exceeded your current quota, please check your plan and billing details. For more information on this error, head to: https://ai.google.dev/gemini-api/docs/rate-limits. To monitor your current usage, head to: https://ai.dev/rate-limit. \\n* Quota exceeded for metric: generativelanguage.googleapis.com/generate_requests_per_model_per_day, limit: 1000, model: gemini-2.5-pro\\nPlease retry in 9h7m24.223552226s.\",\n \"status\": \"RESOURCE_EXHAUSTED\",\n \"details\": [\n {\n \"@type\": \"type.googleapis.com/google.rpc.Help\",\n \"links\": [\n {\n \"description\": \"Learn more about Gemini API quotas\",\n \"url\": \"https://ai.google.dev/gemini-api/docs/rate-limits\"\n }\n ]\n },\n {\n \"@type\": \"type.googleapis.com/google.rpc.QuotaFailure\",\n \"violations\": [\n {\n \"quotaMetric\": \"generativelanguage.googleapis.com/generate_requests_per_model_per_day\",\n \"quotaId\": \"GenerateRequestsPerDayPerProjectPerModel\",\n \"quotaDimensions\": {\n \"model\": \"gemini-2.5-pro\",\n \"location\": \"global\"\n },\n \"quotaValue | gemini-3-flash-preview: exception HTTPSConnectionPool(host='generativelanguage.googleapis.com', port=443): Read timed out. (read timeout=180) | gemini-2.5-flash: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n", + "output_file": "", + "dockerfile_ready": false + }, + { + "tool_name": "trf", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/trf.manual_bundle.txt", + "run_help_command": false, + "status": "failed", + "error": "autogenerate_mcp_tool failed: Gemini request failed across all models. gemini-2.5-pro: HTTP 429 - {\n \"error\": {\n \"code\": 429,\n \"message\": \"You exceeded your current quota, please check your plan and billing details. For more information on this error, head to: https://ai.google.dev/gemini-api/docs/rate-limits. To monitor your current usage, head to: https://ai.dev/rate-limit. \\n* Quota exceeded for metric: generativelanguage.googleapis.com/generate_requests_per_model_per_day, limit: 1000, model: gemini-2.5-pro\\nPlease retry in 9h4m23.05712762s.\",\n \"status\": \"RESOURCE_EXHAUSTED\",\n \"details\": [\n {\n \"@type\": \"type.googleapis.com/google.rpc.Help\",\n \"links\": [\n {\n \"description\": \"Learn more about Gemini API quotas\",\n \"url\": \"https://ai.google.dev/gemini-api/docs/rate-limits\"\n }\n ]\n },\n {\n \"@type\": \"type.googleapis.com/google.rpc.QuotaFailure\",\n \"violations\": [\n {\n \"quotaMetric\": \"generativelanguage.googleapis.com/generate_requests_per_model_per_day\",\n \"quotaId\": \"GenerateRequestsPerDayPerProjectPerModel\",\n \"quotaDimensions\": {\n \"location\": \"global\",\n \"model\": \"gemini-2.5-pro\"\n },\n \"quotaValue\" | gemini-3-flash-preview: exception HTTPSConnectionPool(host='generativelanguage.googleapis.com', port=443): Read timed out. (read timeout=180) | gemini-2.5-flash: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n", + "output_file": "", + "dockerfile_ready": false + }, + { + "tool_name": "trimmomatic", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/trimmomatic.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_trimmomatic/app/trimmomatic_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "ucsc-bedtobigbed", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/ucsc-bedtobigbed.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_ucsc-bedtobigbed/app/ucsc-bedtobigbed_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "ucsc-liftover", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/ucsc-liftover.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_ucsc-liftover/app/ucsc-liftover_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "ucsc-wigtobigwig", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/ucsc-wigtobigwig.manual_bundle.txt", + "run_help_command": false, + "status": "failed", + "error": "autogenerate_mcp_tool failed: Gemini request failed across all models. gemini-2.5-pro: HTTP 429 - {\n \"error\": {\n \"code\": 429,\n \"message\": \"You exceeded your current quota, please check your plan and billing details. For more information on this error, head to: https://ai.google.dev/gemini-api/docs/rate-limits. To monitor your current usage, head to: https://ai.dev/rate-limit. \\n* Quota exceeded for metric: generativelanguage.googleapis.com/generate_requests_per_model_per_day, limit: 1000, model: gemini-2.5-pro\\nPlease retry in 8h59m41.246802677s.\",\n \"status\": \"RESOURCE_EXHAUSTED\",\n \"details\": [\n {\n \"@type\": \"type.googleapis.com/google.rpc.Help\",\n \"links\": [\n {\n \"description\": \"Learn more about Gemini API quotas\",\n \"url\": \"https://ai.google.dev/gemini-api/docs/rate-limits\"\n }\n ]\n },\n {\n \"@type\": \"type.googleapis.com/google.rpc.QuotaFailure\",\n \"violations\": [\n {\n \"quotaMetric\": \"generativelanguage.googleapis.com/generate_requests_per_model_per_day\",\n \"quotaId\": \"GenerateRequestsPerDayPerProjectPerModel\",\n \"quotaDimensions\": {\n \"location\": \"global\",\n \"model\": \"gemini-2.5-pro\"\n },\n \"quotaValu | gemini-3-flash-preview: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-2.5-flash: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n", + "output_file": "", + "dockerfile_ready": false + }, + { + "tool_name": "unifrac", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/unifrac.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_unifrac/app/unifrac_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "usher", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/usher.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_usher/app/usher_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "varscan", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/varscan.manual_bundle.txt", + "run_help_command": false, + "status": "failed", + "error": "autogenerate_mcp_tool failed: Gemini request failed across all models. gemini-2.5-pro: HTTP 429 - {\n \"error\": {\n \"code\": 429,\n \"message\": \"You exceeded your current quota, please check your plan and billing details. For more information on this error, head to: https://ai.google.dev/gemini-api/docs/rate-limits. To monitor your current usage, head to: https://ai.dev/rate-limit. \\n* Quota exceeded for metric: generativelanguage.googleapis.com/generate_requests_per_model_per_day, limit: 1000, model: gemini-2.5-pro\\nPlease retry in 8h55m52.143428344s.\",\n \"status\": \"RESOURCE_EXHAUSTED\",\n \"details\": [\n {\n \"@type\": \"type.googleapis.com/google.rpc.Help\",\n \"links\": [\n {\n \"description\": \"Learn more about Gemini API quotas\",\n \"url\": \"https://ai.google.dev/gemini-api/docs/rate-limits\"\n }\n ]\n },\n {\n \"@type\": \"type.googleapis.com/google.rpc.QuotaFailure\",\n \"violations\": [\n {\n \"quotaMetric\": \"generativelanguage.googleapis.com/generate_requests_per_model_per_day\",\n \"quotaId\": \"GenerateRequestsPerDayPerProjectPerModel\",\n \"quotaDimensions\": {\n \"location\": \"global\",\n \"model\": \"gemini-2.5-pro\"\n },\n \"quotaValu | gemini-3-flash-preview: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-2.5-flash: exception HTTPSConnectionPool(host='generativelanguage.googleapis.com', port=443): Read timed out. (read timeout=180)", + "output_file": "", + "dockerfile_ready": false + }, + { + "tool_name": "vcontact2", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/vcontact2.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_vcontact2/app/vcontact2_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "vsearch", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/vsearch.manual_bundle.txt", + "run_help_command": false, + "status": "failed", + "error": "autogenerate_mcp_tool failed: Gemini request failed across all models. gemini-2.5-pro: HTTP 429 - {\n \"error\": {\n \"code\": 429,\n \"message\": \"You exceeded your current quota, please check your plan and billing details. For more information on this error, head to: https://ai.google.dev/gemini-api/docs/rate-limits. To monitor your current usage, head to: https://ai.dev/rate-limit. \\n* Quota exceeded for metric: generativelanguage.googleapis.com/generate_requests_per_model_per_day, limit: 1000, model: gemini-2.5-pro\\nPlease retry in 8h49m24.630538949s.\",\n \"status\": \"RESOURCE_EXHAUSTED\",\n \"details\": [\n {\n \"@type\": \"type.googleapis.com/google.rpc.Help\",\n \"links\": [\n {\n \"description\": \"Learn more about Gemini API quotas\",\n \"url\": \"https://ai.google.dev/gemini-api/docs/rate-limits\"\n }\n ]\n },\n {\n \"@type\": \"type.googleapis.com/google.rpc.QuotaFailure\",\n \"violations\": [\n {\n \"quotaMetric\": \"generativelanguage.googleapis.com/generate_requests_per_model_per_day\",\n \"quotaId\": \"GenerateRequestsPerDayPerProjectPerModel\",\n \"quotaDimensions\": {\n \"location\": \"global\",\n \"model\": \"gemini-2.5-pro\"\n },\n \"quotaValu | gemini-3-flash-preview: exception HTTPSConnectionPool(host='generativelanguage.googleapis.com', port=443): Read timed out. (read timeout=180) | gemini-2.5-flash: exception HTTPSConnectionPool(host='generativelanguage.googleapis.com', port=443): Read timed out. (read timeout=180)", + "output_file": "", + "dockerfile_ready": false + }, + { + "tool_name": "womtool", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/womtool.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_womtool/app/womtool_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "zdb", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/zdb.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_zdb/app/zdb_server.py", + "dockerfile_ready": true + } +] \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/conversion_report.shard_3_of_4.json b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/conversion_report.shard_3_of_4.json new file mode 100644 index 0000000000000000000000000000000000000000..12e1762d5272731e467beaa8641f0938620aceb9 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/conversion_report.shard_3_of_4.json @@ -0,0 +1,2108 @@ +[ + { + "tool_name": "adapterremoval", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/adapterremoval.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_adapterremoval/app/adapterremoval_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "aragorn", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/aragorn.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_aragorn/app/aragorn_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "art", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/art.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_art/app/art_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "augur", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/augur.manual_bundle.txt", + "run_help_command": false, + "status": "failed", + "error": "refine failed (retry=1): Gemini request failed across all models. gemini-2.5-pro: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-3-flash-preview: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-2.5-flash: exception HTTPSConnectionPool(host='generativelanguage.googleapis.com', port=443): Read timed out. (read timeout=180)", + "output_file": "", + "dockerfile_ready": false + }, + { + "tool_name": "bcbio-gff", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bcbio-gff.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bcbio-gff/app/bcbio-gff_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioawk", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioawk.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioawk/app/bioawk_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconda-utils", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconda-utils.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconda-utils/app/bioconda-utils_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-alabaster.sfe", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-alabaster.sfe.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-alabaster.sfe/app/bioconductor-alabaster.sfe_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-annotationhub", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-annotationhub.manual_bundle.txt", + "run_help_command": false, + "status": "failed", + "error": "refine failed (retry=2): Gemini request failed across all models. gemini-2.5-pro: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-3-flash-preview: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-2.5-flash: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n", + "output_file": "", + "dockerfile_ready": false + }, + { + "tool_name": "bioconductor-benchdamic", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-benchdamic.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-benchdamic/app/bioconductor-benchdamic_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-biomformat", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-biomformat.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-biomformat/app/bioconductor-biomformat_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-bsgenome", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-bsgenome.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-bsgenome/app/bioconductor-bsgenome_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-cdi", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-cdi.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-cdi/app/bioconductor-cdi_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-clusterfoldsimilarity", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-clusterfoldsimilarity.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-clusterfoldsimilarity/app/bioconductor-clusterfoldsimilarity_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-concordexr", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-concordexr.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-concordexr/app/bioconductor-concordexr_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-curatedatlasqueryr", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-curatedatlasqueryr.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-curatedatlasqueryr/app/bioconductor-curatedatlasqueryr_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-decontam", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-decontam.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-decontam/app/bioconductor-decontam_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-delayedmatrixstats", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-delayedmatrixstats.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-delayedmatrixstats/app/bioconductor-delayedmatrixstats_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-dino", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-dino.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-dino/app/bioconductor-dino_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-ebseq", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-ebseq.manual_bundle.txt", + "run_help_command": false, + "status": "failed", + "error": "autogenerate_mcp_tool failed: Gemini request failed across all models. gemini-2.5-pro: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-3-flash-preview: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-2.5-flash: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n", + "output_file": "", + "dockerfile_ready": false + }, + { + "tool_name": "bioconductor-escher", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-escher.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-escher/app/bioconductor-escher_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-geneplotter", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-geneplotter.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-geneplotter/app/bioconductor-geneplotter_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-genomicfeatures", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-genomicfeatures.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-genomicfeatures/app/bioconductor-genomicfeatures_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-ggspavis", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-ggspavis.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-ggspavis/app/bioconductor-ggspavis_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-gosemsim", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-gosemsim.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-gosemsim/app/bioconductor-gosemsim_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-hoodscanr", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-hoodscanr.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-hoodscanr/app/bioconductor-hoodscanr_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-impute", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-impute.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-impute/app/bioconductor-impute_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-jazzpanda", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-jazzpanda.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-jazzpanda/app/bioconductor-jazzpanda_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-mastr", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-mastr.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-mastr/app/bioconductor-mastr_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-mia", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-mia.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-mia/app/bioconductor-mia_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-multiassayexperiment", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-multiassayexperiment.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-multiassayexperiment/app/bioconductor-multiassayexperiment_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-noiseq", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-noiseq.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-noiseq/app/bioconductor-noiseq_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-org.mm.eg.db", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-org.mm.eg.db.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-org.mm.eg.db/app/bioconductor-org.mm.eg.db_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-poem", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-poem.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-poem/app/bioconductor-poem_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-rbgl", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-rbgl.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-rbgl/app/bioconductor-rbgl_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-rhdf5", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-rhdf5.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-rhdf5/app/bioconductor-rhdf5_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-rsamtools", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-rsamtools.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-rsamtools/app/bioconductor-rsamtools_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-s4vectors", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-s4vectors.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-s4vectors/app/bioconductor-s4vectors_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-scarray.sat", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-scarray.sat.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-scarray.sat/app/bioconductor-scarray.sat_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-scdotplot", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-scdotplot.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-scdotplot/app/bioconductor-scdotplot_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-scran", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-scran.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-scran/app/bioconductor-scran_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-scvir", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-scvir.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-scvir/app/bioconductor-scvir_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-singlecellexperiment", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-singlecellexperiment.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-singlecellexperiment/app/bioconductor-singlecellexperiment_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-sosta", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-sosta.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-sosta/app/bioconductor-sosta_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-spari", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-spari.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-spari/app/bioconductor-spari_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-spatialdatasets", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-spatialdatasets.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-spatialdatasets/app/bioconductor-spatialdatasets_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-spatialfda", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-spatialfda.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-spatialfda/app/bioconductor-spatialfda_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-spatialsimgp", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-spatialsimgp.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-spatialsimgp/app/bioconductor-spatialsimgp_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-spoon", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-spoon.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-spoon/app/bioconductor-spoon_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-statial", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-statial.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-statial/app/bioconductor-statial_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-subcellularspatialdata", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-subcellularspatialdata.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-subcellularspatialdata/app/bioconductor-subcellularspatialdata_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-tenxvisiumdata", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-tenxvisiumdata.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-tenxvisiumdata/app/bioconductor-tenxvisiumdata_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-tpsvg", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-tpsvg.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-tpsvg/app/bioconductor-tpsvg_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-variantannotation", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-variantannotation.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-variantannotation/app/bioconductor-variantannotation_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-voyager", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-voyager.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-voyager/app/bioconductor-voyager_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bioconductor-xenlite", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bioconductor-xenlite.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-xenlite/app/bioconductor-xenlite_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "biopython", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/biopython.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_biopython/app/biopython_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bowtie2", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bowtie2.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bowtie2/app/bowtie2_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "bx-python", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/bx-python.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bx-python/app/bx-python_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "cdbtools", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/cdbtools.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_cdbtools/app/cdbtools_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "cellsnake", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/cellsnake.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_cellsnake/app/cellsnake_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "circexplorer2", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/circexplorer2.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_circexplorer2/app/circexplorer2_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "cmappy", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/cmappy.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_cmappy/app/cmappy_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "constellations", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/constellations.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_constellations/app/constellations_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "cosg", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/cosg.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_cosg/app/cosg_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "crisprme", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/crisprme.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_crisprme/app/crisprme_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "cwltool", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/cwltool.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_cwltool/app/cwltool_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "ddocent", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/ddocent.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_ddocent/app/ddocent_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "delly", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/delly.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_delly/app/delly_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "doubletdetection", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/doubletdetection.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_doubletdetection/app/doubletdetection_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "easy_vitessce", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/easy_vitessce.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_easy_vitessce/app/easy_vitessce_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "ensembl-vep", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/ensembl-vep.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_ensembl-vep/app/ensembl-vep_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "eva-sub-cli", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/eva-sub-cli.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_eva-sub-cli/app/eva-sub-cli_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "fastani", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/fastani.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_fastani/app/fastani_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "fasttree", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/fasttree.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_fasttree/app/fasttree_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "flagx", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/flagx.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_flagx/app/flagx_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "ftputil", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/ftputil.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_ftputil/app/ftputil_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "gatk4", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/gatk4.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_gatk4/app/gatk4_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "gemini", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/gemini.manual_bundle.txt", + "run_help_command": false, + "status": "failed", + "error": "autogenerate_mcp_tool failed: Gemini request failed across all models. gemini-2.5-pro: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-3-flash-preview: exception HTTPSConnectionPool(host='generativelanguage.googleapis.com', port=443): Read timed out. (read timeout=180) | gemini-2.5-flash: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n", + "output_file": "", + "dockerfile_ready": false + }, + { + "tool_name": "genenotebook", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/genenotebook.manual_bundle.txt", + "run_help_command": false, + "status": "failed", + "error": "autogenerate_mcp_tool failed: Gemini request failed across all models. gemini-2.5-pro: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-3-flash-preview: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-2.5-flash: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n", + "output_file": "", + "dockerfile_ready": false + }, + { + "tool_name": "genometools-genometools", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/genometools-genometools.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_genometools-genometools/app/genometools-genometools_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "gimmemotifs", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/gimmemotifs.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_gimmemotifs/app/gimmemotifs_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "gnuplot", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/gnuplot.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_gnuplot/app/gnuplot_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "gromacs", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/gromacs.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_gromacs/app/gromacs_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "hhsuite", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/hhsuite.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_hhsuite/app/hhsuite_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "hmmer", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/hmmer.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_hmmer/app/hmmer_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "humann2", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/humann2.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_humann2/app/humann2_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "illumina-interop", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/illumina-interop.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_illumina-interop/app/illumina-interop_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "involucro", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/involucro.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_involucro/app/involucro_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "iqtree", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/iqtree.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_iqtree/app/iqtree_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "java-jdk", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/java-jdk.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_java-jdk/app/java-jdk_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "jq", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/jq.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_jq/app/jq_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "kallisto", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/kallisto.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_kallisto/app/kallisto_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "kmc", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/kmc.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_kmc/app/kmc_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "last", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/last.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_last/app/last_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "libdb", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/libdb.manual_bundle.txt", + "run_help_command": false, + "status": "failed", + "error": "autogenerate_mcp_tool failed: Gemini request failed across all models. gemini-2.5-pro: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-3-flash-preview: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-2.5-flash: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n", + "output_file": "", + "dockerfile_ready": false + }, + { + "tool_name": "macs2", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/macs2.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_macs2/app/macs2_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "mappy", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/mappy.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_mappy/app/mappy_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "mcl", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/mcl.manual_bundle.txt", + "run_help_command": false, + "status": "failed", + "error": "autogenerate_mcp_tool failed: Gemini request failed across all models. gemini-2.5-pro: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-3-flash-preview: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-2.5-flash: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n", + "output_file": "", + "dockerfile_ready": false + }, + { + "tool_name": "mentalist", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/mentalist.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_mentalist/app/mentalist_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "mikado", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/mikado.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_mikado/app/mikado_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "minvar", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/minvar.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_minvar/app/minvar_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "mmtf-python", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/mmtf-python.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_mmtf-python/app/mmtf-python_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "mothur", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/mothur.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_mothur/app/mothur_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "multiqc", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/multiqc.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_multiqc/app/multiqc_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "munkres", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/munkres.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_munkres/app/munkres_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "nanocomp", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/nanocomp.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_nanocomp/app/nanocomp_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "nanopolish", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/nanopolish.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_nanopolish/app/nanopolish_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "ncbi-ngs-sdk", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/ncbi-ngs-sdk.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_ncbi-ngs-sdk/app/ncbi-ngs-sdk_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "nextclade", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/nextclade.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_nextclade/app/nextclade_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "ngmlr", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/ngmlr.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_ngmlr/app/ngmlr_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "novoalign", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/novoalign.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_novoalign/app/novoalign_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "orthofinder", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/orthofinder.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_orthofinder/app/orthofinder_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "paml", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/paml.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_paml/app/paml_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "pangolin", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/pangolin.manual_bundle.txt", + "run_help_command": false, + "status": "failed", + "error": "refine failed (retry=1): Gemini request failed across all models. gemini-2.5-pro: exception HTTPSConnectionPool(host='generativelanguage.googleapis.com', port=443): Read timed out. (read timeout=180) | gemini-3-flash-preview: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-2.5-flash: exception HTTPSConnectionPool(host='generativelanguage.googleapis.com', port=443): Read timed out. (read timeout=180)", + "output_file": "", + "dockerfile_ready": false + }, + { + "tool_name": "paste-bio", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/paste-bio.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_paste-bio/app/paste-bio_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "peptides", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/peptides.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_peptides/app/peptides_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-alien-build", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-alien-build.manual_bundle.txt", + "run_help_command": false, + "status": "failed", + "error": "autogenerate_mcp_tool failed: Gemini request failed across all models. gemini-2.5-pro: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-3-flash-preview: exception HTTPSConnectionPool(host='generativelanguage.googleapis.com', port=443): Read timed out. (read timeout=180) | gemini-2.5-flash: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n", + "output_file": "", + "dockerfile_ready": false + }, + { + "tool_name": "perl-appconfig", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-appconfig.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-appconfig/app/perl-appconfig_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-base", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-base.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-base/app/perl-base_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-bio-featureio", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-bio-featureio.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-bio-featureio/app/perl-bio-featureio_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-bio-tools-phylo-paml", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-bio-tools-phylo-paml.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-bio-tools-phylo-paml/app/perl-bio-tools-phylo-paml_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-bioperl-core", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-bioperl-core.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-bioperl-core/app/perl-bioperl-core_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-cache-cache", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-cache-cache.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-cache-cache/app/perl-cache-cache_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-class-data-inheritable", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-class-data-inheritable.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-class-data-inheritable/app/perl-class-data-inheritable_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-class-method-modifiers", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-class-method-modifiers.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-class-method-modifiers/app/perl-class-method-modifiers_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-compress-raw-zlib", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-compress-raw-zlib.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-compress-raw-zlib/app/perl-compress-raw-zlib_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-convert-binhex", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-convert-binhex.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-convert-binhex/app/perl-convert-binhex_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-data-dumper", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-data-dumper.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-data-dumper/app/perl-data-dumper_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-db-file", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-db-file.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-db-file/app/perl-db-file_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-devel-globaldestruction", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-devel-globaldestruction.manual_bundle.txt", + "run_help_command": false, + "status": "failed", + "error": "autogenerate_mcp_tool failed: Gemini request failed across all models. gemini-2.5-pro: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-3-flash-preview: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-2.5-flash: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n", + "output_file": "", + "dockerfile_ready": false + }, + { + "tool_name": "perl-digest-md5", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-digest-md5.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-digest-md5/app/perl-digest-md5_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-dynaloader", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-dynaloader.manual_bundle.txt", + "run_help_command": false, + "status": "failed", + "error": "autogenerate_mcp_tool failed: Gemini request failed across all models. gemini-2.5-pro: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-3-flash-preview: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-2.5-flash: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n", + "output_file": "", + "dockerfile_ready": false + }, + { + "tool_name": "perl-error", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-error.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-error/app/perl-error_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-exporter-tiny", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-exporter-tiny.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-exporter-tiny/app/perl-exporter-tiny_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-extutils-parsexs", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-extutils-parsexs.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-extutils-parsexs/app/perl-extutils-parsexs_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-file-slurp-tiny", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-file-slurp-tiny.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-file-slurp-tiny/app/perl-file-slurp-tiny_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-file-which", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-file-which.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-file-which/app/perl-file-which_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-getopt-long", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-getopt-long.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-getopt-long/app/perl-getopt-long_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-html-entities-numbered", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-html-entities-numbered.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-html-entities-numbered/app/perl-html-entities-numbered_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-html-tagset", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-html-tagset.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-html-tagset/app/perl-html-tagset_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-http-cookies", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-http-cookies.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-http-cookies/app/perl-http-cookies_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-http-negotiate", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-http-negotiate.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-http-negotiate/app/perl-http-negotiate_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-io-compress", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-io-compress.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-io-compress/app/perl-io-compress_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-io-string", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-io-string.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-io-string/app/perl-io-string_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-ipc-cmd", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-ipc-cmd.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-ipc-cmd/app/perl-ipc-cmd_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-json", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-json.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-json/app/perl-json_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-libwww-perl", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-libwww-perl.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-libwww-perl/app/perl-libwww-perl_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-locale-maketext-simple", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-locale-maketext-simple.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-locale-maketext-simple/app/perl-locale-maketext-simple_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-mailtools", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-mailtools.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-mailtools/app/perl-mailtools_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-math-random", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-math-random.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-math-random/app/perl-math-random_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-mce", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-mce.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-mce/app/perl-mce_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-mime-types", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-mime-types.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-mime-types/app/perl-mime-types_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-module-implementation", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-module-implementation.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-module-implementation/app/perl-module-implementation_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-module-runtime", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-module-runtime.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-module-runtime/app/perl-module-runtime_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-mozilla-ca", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-mozilla-ca.manual_bundle.txt", + "run_help_command": false, + "status": "failed", + "error": "autogenerate_mcp_tool failed: Gemini request failed across all models. gemini-2.5-pro: HTTP 429 - {\n \"error\": {\n \"code\": 429,\n \"message\": \"You exceeded your current quota, please check your plan and billing details. For more information on this error, head to: https://ai.google.dev/gemini-api/docs/rate-limits. To monitor your current usage, head to: https://ai.dev/rate-limit. \\n* Quota exceeded for metric: generativelanguage.googleapis.com/generate_requests_per_model_per_day, limit: 1000, model: gemini-2.5-pro\\nPlease retry in 9h51m35.507712077s.\",\n \"status\": \"RESOURCE_EXHAUSTED\",\n \"details\": [\n {\n \"@type\": \"type.googleapis.com/google.rpc.Help\",\n \"links\": [\n {\n \"description\": \"Learn more about Gemini API quotas\",\n \"url\": \"https://ai.google.dev/gemini-api/docs/rate-limits\"\n }\n ]\n },\n {\n \"@type\": \"type.googleapis.com/google.rpc.QuotaFailure\",\n \"violations\": [\n {\n \"quotaMetric\": \"generativelanguage.googleapis.com/generate_requests_per_model_per_day\",\n \"quotaId\": \"GenerateRequestsPerDayPerProjectPerModel\",\n \"quotaDimensions\": {\n \"location\": \"global\",\n \"model\": \"gemini-2.5-pro\"\n },\n \"quotaValu | gemini-3-flash-preview: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-2.5-flash: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n", + "output_file": "", + "dockerfile_ready": false + }, + { + "tool_name": "perl-ntlm", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-ntlm.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-ntlm/app/perl-ntlm_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-package-stash", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-package-stash.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-package-stash/app/perl-package-stash_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-params-util", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-params-util.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-params-util/app/perl-params-util_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-path-tiny", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-path-tiny.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-path-tiny/app/perl-path-tiny_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-pod-escapes", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-pod-escapes.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-pod-escapes/app/perl-pod-escapes_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-regexp-common", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-regexp-common.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-regexp-common/app/perl-regexp-common_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-sereal-decoder", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-sereal-decoder.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-sereal-decoder/app/perl-sereal-decoder_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-set-scalar", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-set-scalar.manual_bundle.txt", + "run_help_command": false, + "status": "failed", + "error": "autogenerate_mcp_tool failed: Gemini request failed across all models. gemini-2.5-pro: HTTP 429 - {\n \"error\": {\n \"code\": 429,\n \"message\": \"You exceeded your current quota, please check your plan and billing details. For more information on this error, head to: https://ai.google.dev/gemini-api/docs/rate-limits. To monitor your current usage, head to: https://ai.dev/rate-limit. \\n* Quota exceeded for metric: generativelanguage.googleapis.com/generate_requests_per_model_per_day, limit: 1000, model: gemini-2.5-pro\\nPlease retry in 9h37m47.390002612s.\",\n \"status\": \"RESOURCE_EXHAUSTED\",\n \"details\": [\n {\n \"@type\": \"type.googleapis.com/google.rpc.Help\",\n \"links\": [\n {\n \"description\": \"Learn more about Gemini API quotas\",\n \"url\": \"https://ai.google.dev/gemini-api/docs/rate-limits\"\n }\n ]\n },\n {\n \"@type\": \"type.googleapis.com/google.rpc.QuotaFailure\",\n \"violations\": [\n {\n \"quotaMetric\": \"generativelanguage.googleapis.com/generate_requests_per_model_per_day\",\n \"quotaId\": \"GenerateRequestsPerDayPerProjectPerModel\",\n \"quotaDimensions\": {\n \"location\": \"global\",\n \"model\": \"gemini-2.5-pro\"\n },\n \"quotaValu | gemini-3-flash-preview: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-2.5-flash: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n", + "output_file": "", + "dockerfile_ready": false + }, + { + "tool_name": "perl-spreadsheet-parseexcel", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-spreadsheet-parseexcel.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-spreadsheet-parseexcel/app/perl-spreadsheet-parseexcel_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-storable", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-storable.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-storable/app/perl-storable_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-sub-install", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-sub-install.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-sub-install/app/perl-sub-install_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-svg", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-svg.manual_bundle.txt", + "run_help_command": false, + "status": "failed", + "error": "autogenerate_mcp_tool failed: Gemini request failed across all models. gemini-2.5-pro: HTTP 429 - {\n \"error\": {\n \"code\": 429,\n \"message\": \"You exceeded your current quota, please check your plan and billing details. For more information on this error, head to: https://ai.google.dev/gemini-api/docs/rate-limits. To monitor your current usage, head to: https://ai.dev/rate-limit. \\n* Quota exceeded for metric: generativelanguage.googleapis.com/generate_requests_per_model_per_day, limit: 1000, model: gemini-2.5-pro\\nPlease retry in 9h34m36.453030836s.\",\n \"status\": \"RESOURCE_EXHAUSTED\",\n \"details\": [\n {\n \"@type\": \"type.googleapis.com/google.rpc.Help\",\n \"links\": [\n {\n \"description\": \"Learn more about Gemini API quotas\",\n \"url\": \"https://ai.google.dev/gemini-api/docs/rate-limits\"\n }\n ]\n },\n {\n \"@type\": \"type.googleapis.com/google.rpc.QuotaFailure\",\n \"violations\": [\n {\n \"quotaMetric\": \"generativelanguage.googleapis.com/generate_requests_per_model_per_day\",\n \"quotaId\": \"GenerateRequestsPerDayPerProjectPerModel\",\n \"quotaDimensions\": {\n \"model\": \"gemini-2.5-pro\",\n \"location\": \"global\"\n },\n \"quotaValu | gemini-3-flash-preview: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-2.5-flash: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n", + "output_file": "", + "dockerfile_ready": false + }, + { + "tool_name": "perl-term-table", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-term-table.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-term-table/app/perl-term-table_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-test-exception", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-test-exception.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-test-exception/app/perl-test-exception_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-test-more", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-test-more.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-test-more/app/perl-test-more_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-test2-suite", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-test2-suite.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-test2-suite/app/perl-test2-suite_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-text-parsewords", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-text-parsewords.manual_bundle.txt", + "run_help_command": false, + "status": "failed", + "error": "autogenerate_mcp_tool failed: Gemini request failed across all models. gemini-2.5-pro: HTTP 429 - {\n \"error\": {\n \"code\": 429,\n \"message\": \"You exceeded your current quota, please check your plan and billing details. For more information on this error, head to: https://ai.google.dev/gemini-api/docs/rate-limits. To monitor your current usage, head to: https://ai.dev/rate-limit. \\n* Quota exceeded for metric: generativelanguage.googleapis.com/generate_requests_per_model_per_day, limit: 1000, model: gemini-2.5-pro\\nPlease retry in 9h33m9.709708464s.\",\n \"status\": \"RESOURCE_EXHAUSTED\",\n \"details\": [\n {\n \"@type\": \"type.googleapis.com/google.rpc.Help\",\n \"links\": [\n {\n \"description\": \"Learn more about Gemini API quotas\",\n \"url\": \"https://ai.google.dev/gemini-api/docs/rate-limits\"\n }\n ]\n },\n {\n \"@type\": \"type.googleapis.com/google.rpc.QuotaFailure\",\n \"violations\": [\n {\n \"quotaMetric\": \"generativelanguage.googleapis.com/generate_requests_per_model_per_day\",\n \"quotaId\": \"GenerateRequestsPerDayPerProjectPerModel\",\n \"quotaDimensions\": {\n \"location\": \"global\",\n \"model\": \"gemini-2.5-pro\"\n },\n \"quotaValue | gemini-3-flash-preview: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-2.5-flash: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n", + "output_file": "", + "dockerfile_ready": false + }, + { + "tool_name": "perl-time-local", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-time-local.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-time-local/app/perl-time-local_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-type-tiny", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-type-tiny.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-type-tiny/app/perl-type-tiny_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-url-encode", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-url-encode.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-url-encode/app/perl-url-encode_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-xml-dom-xpath", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-xml-dom-xpath.manual_bundle.txt", + "run_help_command": false, + "status": "failed", + "error": "autogenerate_mcp_tool failed: Gemini request failed across all models. gemini-2.5-pro: HTTP 429 - {\n \"error\": {\n \"code\": 429,\n \"message\": \"You exceeded your current quota, please check your plan and billing details. For more information on this error, head to: https://ai.google.dev/gemini-api/docs/rate-limits. To monitor your current usage, head to: https://ai.dev/rate-limit. \\n* Quota exceeded for metric: generativelanguage.googleapis.com/generate_requests_per_model_per_day, limit: 1000, model: gemini-2.5-pro\\nPlease retry in 9h31m39.277195145s.\",\n \"status\": \"RESOURCE_EXHAUSTED\",\n \"details\": [\n {\n \"@type\": \"type.googleapis.com/google.rpc.Help\",\n \"links\": [\n {\n \"description\": \"Learn more about Gemini API quotas\",\n \"url\": \"https://ai.google.dev/gemini-api/docs/rate-limits\"\n }\n ]\n },\n {\n \"@type\": \"type.googleapis.com/google.rpc.QuotaFailure\",\n \"violations\": [\n {\n \"quotaMetric\": \"generativelanguage.googleapis.com/generate_requests_per_model_per_day\",\n \"quotaId\": \"GenerateRequestsPerDayPerProjectPerModel\",\n \"quotaDimensions\": {\n \"location\": \"global\",\n \"model\": \"gemini-2.5-pro\"\n },\n \"quotaValu | gemini-3-flash-preview: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-2.5-flash: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n", + "output_file": "", + "dockerfile_ready": false + }, + { + "tool_name": "perl-xml-namespacesupport", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-xml-namespacesupport.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-xml-namespacesupport/app/perl-xml-namespacesupport_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-xml-sax-base", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-xml-sax-base.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-xml-sax-base/app/perl-xml-sax-base_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-xml-twig", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-xml-twig.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-xml-twig/app/perl-xml-twig_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "perl-xsloader", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/perl-xsloader.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-xsloader/app/perl-xsloader_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "picard", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/picard.manual_bundle.txt", + "run_help_command": false, + "status": "failed", + "error": "autogenerate_mcp_tool failed: Gemini request failed across all models. gemini-2.5-pro: HTTP 429 - {\n \"error\": {\n \"code\": 429,\n \"message\": \"You exceeded your current quota, please check your plan and billing details. For more information on this error, head to: https://ai.google.dev/gemini-api/docs/rate-limits. To monitor your current usage, head to: https://ai.dev/rate-limit. \\n* Quota exceeded for metric: generativelanguage.googleapis.com/generate_requests_per_model_per_day, limit: 1000, model: gemini-2.5-pro\\nPlease retry in 9h29m37.050845345s.\",\n \"status\": \"RESOURCE_EXHAUSTED\",\n \"details\": [\n {\n \"@type\": \"type.googleapis.com/google.rpc.Help\",\n \"links\": [\n {\n \"description\": \"Learn more about Gemini API quotas\",\n \"url\": \"https://ai.google.dev/gemini-api/docs/rate-limits\"\n }\n ]\n },\n {\n \"@type\": \"type.googleapis.com/google.rpc.QuotaFailure\",\n \"violations\": [\n {\n \"quotaMetric\": \"generativelanguage.googleapis.com/generate_requests_per_model_per_day\",\n \"quotaId\": \"GenerateRequestsPerDayPerProjectPerModel\",\n \"quotaDimensions\": {\n \"location\": \"global\",\n \"model\": \"gemini-2.5-pro\"\n },\n \"quotaValu | gemini-3-flash-preview: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-2.5-flash: exception HTTPSConnectionPool(host='generativelanguage.googleapis.com', port=443): Read timed out. (read timeout=180)", + "output_file": "", + "dockerfile_ready": false + }, + { + "tool_name": "planemo", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/planemo.manual_bundle.txt", + "run_help_command": false, + "status": "failed", + "error": "autogenerate_mcp_tool failed: Gemini request failed across all models. gemini-2.5-pro: HTTP 429 - {\n \"error\": {\n \"code\": 429,\n \"message\": \"You exceeded your current quota, please check your plan and billing details. For more information on this error, head to: https://ai.google.dev/gemini-api/docs/rate-limits. To monitor your current usage, head to: https://ai.dev/rate-limit. \\n* Quota exceeded for metric: generativelanguage.googleapis.com/generate_requests_per_model_per_day, limit: 1000, model: gemini-2.5-pro\\nPlease retry in 9h26m35.864337402s.\",\n \"status\": \"RESOURCE_EXHAUSTED\",\n \"details\": [\n {\n \"@type\": \"type.googleapis.com/google.rpc.Help\",\n \"links\": [\n {\n \"description\": \"Learn more about Gemini API quotas\",\n \"url\": \"https://ai.google.dev/gemini-api/docs/rate-limits\"\n }\n ]\n },\n {\n \"@type\": \"type.googleapis.com/google.rpc.QuotaFailure\",\n \"violations\": [\n {\n \"quotaMetric\": \"generativelanguage.googleapis.com/generate_requests_per_model_per_day\",\n \"quotaId\": \"GenerateRequestsPerDayPerProjectPerModel\",\n \"quotaDimensions\": {\n \"location\": \"global\",\n \"model\": \"gemini-2.5-pro\"\n },\n \"quotaValu | gemini-3-flash-preview: exception HTTPSConnectionPool(host='generativelanguage.googleapis.com', port=443): Read timed out. (read timeout=180) | gemini-2.5-flash: exception HTTPSConnectionPool(host='generativelanguage.googleapis.com', port=443): Read timed out. (read timeout=180)", + "output_file": "", + "dockerfile_ready": false + }, + { + "tool_name": "pplacer", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/pplacer.manual_bundle.txt", + "run_help_command": false, + "status": "failed", + "error": "autogenerate_mcp_tool failed: Gemini request failed across all models. gemini-2.5-pro: HTTP 429 - {\n \"error\": {\n \"code\": 429,\n \"message\": \"You exceeded your current quota, please check your plan and billing details. For more information on this error, head to: https://ai.google.dev/gemini-api/docs/rate-limits. To monitor your current usage, head to: https://ai.dev/rate-limit. \\n* Quota exceeded for metric: generativelanguage.googleapis.com/generate_requests_per_model_per_day, limit: 1000, model: gemini-2.5-pro\\nPlease retry in 9h20m35.476439951s.\",\n \"status\": \"RESOURCE_EXHAUSTED\",\n \"details\": [\n {\n \"@type\": \"type.googleapis.com/google.rpc.Help\",\n \"links\": [\n {\n \"description\": \"Learn more about Gemini API quotas\",\n \"url\": \"https://ai.google.dev/gemini-api/docs/rate-limits\"\n }\n ]\n },\n {\n \"@type\": \"type.googleapis.com/google.rpc.QuotaFailure\",\n \"violations\": [\n {\n \"quotaMetric\": \"generativelanguage.googleapis.com/generate_requests_per_model_per_day\",\n \"quotaId\": \"GenerateRequestsPerDayPerProjectPerModel\",\n \"quotaDimensions\": {\n \"model\": \"gemini-2.5-pro\",\n \"location\": \"global\"\n },\n \"quotaValu | gemini-3-flash-preview: exception HTTPSConnectionPool(host='generativelanguage.googleapis.com', port=443): Read timed out. (read timeout=180) | gemini-2.5-flash: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n", + "output_file": "", + "dockerfile_ready": false + }, + { + "tool_name": "prodigal", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/prodigal.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_prodigal/app/prodigal_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "proteomiqon-peptidedb", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/proteomiqon-peptidedb.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_proteomiqon-peptidedb/app/proteomiqon-peptidedb_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "pybedtools", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/pybedtools.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_pybedtools/app/pybedtools_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "pyfastx", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/pyfastx.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_pyfastx/app/pyfastx_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "pyrodigal", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/pyrodigal.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_pyrodigal/app/pyrodigal_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "pyspoa", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/pyspoa.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_pyspoa/app/pyspoa_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "pyvcf", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/pyvcf.manual_bundle.txt", + "run_help_command": false, + "status": "failed", + "error": "autogenerate_mcp_tool failed: Gemini request failed across all models. gemini-2.5-pro: HTTP 429 - {\n \"error\": {\n \"code\": 429,\n \"message\": \"You exceeded your current quota, please check your plan and billing details. For more information on this error, head to: https://ai.google.dev/gemini-api/docs/rate-limits. To monitor your current usage, head to: https://ai.dev/rate-limit. \\n* Quota exceeded for metric: generativelanguage.googleapis.com/generate_requests_per_model_per_day, limit: 1000, model: gemini-2.5-pro\\nPlease retry in 9h14m45.164592141s.\",\n \"status\": \"RESOURCE_EXHAUSTED\",\n \"details\": [\n {\n \"@type\": \"type.googleapis.com/google.rpc.Help\",\n \"links\": [\n {\n \"description\": \"Learn more about Gemini API quotas\",\n \"url\": \"https://ai.google.dev/gemini-api/docs/rate-limits\"\n }\n ]\n },\n {\n \"@type\": \"type.googleapis.com/google.rpc.QuotaFailure\",\n \"violations\": [\n {\n \"quotaMetric\": \"generativelanguage.googleapis.com/generate_requests_per_model_per_day\",\n \"quotaId\": \"GenerateRequestsPerDayPerProjectPerModel\",\n \"quotaDimensions\": {\n \"model\": \"gemini-2.5-pro\",\n \"location\": \"global\"\n },\n \"quotaValu | gemini-3-flash-preview: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-2.5-flash: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n", + "output_file": "", + "dockerfile_ready": false + }, + { + "tool_name": "qualimap", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/qualimap.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_qualimap/app/qualimap_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "r-archr", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/r-archr.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_r-archr/app/r-archr_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "r-cdseq", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/r-cdseq.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_r-cdseq/app/r-cdseq_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "r-goalie", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/r-goalie.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_r-goalie/app/r-goalie_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "r-saige", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/r-saige.manual_bundle.txt", + "run_help_command": false, + "status": "failed", + "error": "autogenerate_mcp_tool failed: Gemini request failed across all models. gemini-2.5-pro: HTTP 429 - {\n \"error\": {\n \"code\": 429,\n \"message\": \"You exceeded your current quota, please check your plan and billing details. For more information on this error, head to: https://ai.google.dev/gemini-api/docs/rate-limits. To monitor your current usage, head to: https://ai.dev/rate-limit. \\n* Quota exceeded for metric: generativelanguage.googleapis.com/generate_requests_per_model_per_day, limit: 1000, model: gemini-2.5-pro\\nPlease retry in 9h13m15.575664185s.\",\n \"status\": \"RESOURCE_EXHAUSTED\",\n \"details\": [\n {\n \"@type\": \"type.googleapis.com/google.rpc.Help\",\n \"links\": [\n {\n \"description\": \"Learn more about Gemini API quotas\",\n \"url\": \"https://ai.google.dev/gemini-api/docs/rate-limits\"\n }\n ]\n },\n {\n \"@type\": \"type.googleapis.com/google.rpc.QuotaFailure\",\n \"violations\": [\n {\n \"quotaMetric\": \"generativelanguage.googleapis.com/generate_requests_per_model_per_day\",\n \"quotaId\": \"GenerateRequestsPerDayPerProjectPerModel\",\n \"quotaDimensions\": {\n \"location\": \"global\",\n \"model\": \"gemini-2.5-pro\"\n },\n \"quotaValu | gemini-3-flash-preview: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-2.5-flash: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n", + "output_file": "", + "dockerfile_ready": false + }, + { + "tool_name": "r-seurat-disk", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/r-seurat-disk.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_r-seurat-disk/app/r-seurat-disk_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "repeatmasker", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/repeatmasker.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_repeatmasker/app/repeatmasker_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "rpsbproc", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/rpsbproc.manual_bundle.txt", + "run_help_command": false, + "status": "failed", + "error": "autogenerate_mcp_tool failed: Gemini request failed across all models. gemini-2.5-pro: HTTP 429 - {\n \"error\": {\n \"code\": 429,\n \"message\": \"You exceeded your current quota, please check your plan and billing details. For more information on this error, head to: https://ai.google.dev/gemini-api/docs/rate-limits. To monitor your current usage, head to: https://ai.dev/rate-limit. \\n* Quota exceeded for metric: generativelanguage.googleapis.com/generate_requests_per_model_per_day, limit: 1000, model: gemini-2.5-pro\\nPlease retry in 9h9m27.688191477s.\",\n \"status\": \"RESOURCE_EXHAUSTED\",\n \"details\": [\n {\n \"@type\": \"type.googleapis.com/google.rpc.Help\",\n \"links\": [\n {\n \"description\": \"Learn more about Gemini API quotas\",\n \"url\": \"https://ai.google.dev/gemini-api/docs/rate-limits\"\n }\n ]\n },\n {\n \"@type\": \"type.googleapis.com/google.rpc.QuotaFailure\",\n \"violations\": [\n {\n \"quotaMetric\": \"generativelanguage.googleapis.com/generate_requests_per_model_per_day\",\n \"quotaId\": \"GenerateRequestsPerDayPerProjectPerModel\",\n \"quotaDimensions\": {\n \"model\": \"gemini-2.5-pro\",\n \"location\": \"global\"\n },\n \"quotaValue | gemini-3-flash-preview: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-2.5-flash: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n", + "output_file": "", + "dockerfile_ready": false + }, + { + "tool_name": "rseqc", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/rseqc.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_rseqc/app/rseqc_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "salmon", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/salmon.manual_bundle.txt", + "run_help_command": false, + "status": "failed", + "error": "autogenerate_mcp_tool failed: Gemini request failed across all models. gemini-2.5-pro: HTTP 429 - {\n \"error\": {\n \"code\": 429,\n \"message\": \"You exceeded your current quota, please check your plan and billing details. For more information on this error, head to: https://ai.google.dev/gemini-api/docs/rate-limits. To monitor your current usage, head to: https://ai.dev/rate-limit. \\n* Quota exceeded for metric: generativelanguage.googleapis.com/generate_requests_per_model_per_day, limit: 1000, model: gemini-2.5-pro\\nPlease retry in 9h8m42.829184629s.\",\n \"status\": \"RESOURCE_EXHAUSTED\",\n \"details\": [\n {\n \"@type\": \"type.googleapis.com/google.rpc.Help\",\n \"links\": [\n {\n \"description\": \"Learn more about Gemini API quotas\",\n \"url\": \"https://ai.google.dev/gemini-api/docs/rate-limits\"\n }\n ]\n },\n {\n \"@type\": \"type.googleapis.com/google.rpc.QuotaFailure\",\n \"violations\": [\n {\n \"quotaMetric\": \"generativelanguage.googleapis.com/generate_requests_per_model_per_day\",\n \"quotaId\": \"GenerateRequestsPerDayPerProjectPerModel\",\n \"quotaDimensions\": {\n \"model\": \"gemini-2.5-pro\",\n \"location\": \"global\"\n },\n \"quotaValue | gemini-3-flash-preview: exception HTTPSConnectionPool(host='generativelanguage.googleapis.com', port=443): Read timed out. (read timeout=180) | gemini-2.5-flash: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n", + "output_file": "", + "dockerfile_ready": false + }, + { + "tool_name": "samsift", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/samsift.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_samsift/app/samsift_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "scanpy-cli", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/scanpy-cli.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_scanpy-cli/app/scanpy-cli_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "scglue", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/scglue.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_scglue/app/scglue_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "scrnasim-toolz", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/scrnasim-toolz.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_scrnasim-toolz/app/scrnasim-toolz_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "scvi", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/scvi.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_scvi/app/scvi_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "scxmatch", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/scxmatch.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_scxmatch/app/scxmatch_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "segemehl", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/segemehl.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_segemehl/app/segemehl_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "seqcluster", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/seqcluster.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_seqcluster/app/seqcluster_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "sevenbridges-python", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/sevenbridges-python.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_sevenbridges-python/app/sevenbridges-python_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "sina", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/sina.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_sina/app/sina_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "snakemake-interface-common", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/snakemake-interface-common.manual_bundle.txt", + "run_help_command": false, + "status": "failed", + "error": "autogenerate_mcp_tool failed: Gemini request failed across all models. gemini-2.5-pro: HTTP 429 - {\n \"error\": {\n \"code\": 429,\n \"message\": \"You exceeded your current quota, please check your plan and billing details. For more information on this error, head to: https://ai.google.dev/gemini-api/docs/rate-limits. To monitor your current usage, head to: https://ai.dev/rate-limit. \\n* Quota exceeded for metric: generativelanguage.googleapis.com/generate_requests_per_model_per_day, limit: 1000, model: gemini-2.5-pro\\nPlease retry in 9h2m14.679779784s.\",\n \"status\": \"RESOURCE_EXHAUSTED\",\n \"details\": [\n {\n \"@type\": \"type.googleapis.com/google.rpc.Help\",\n \"links\": [\n {\n \"description\": \"Learn more about Gemini API quotas\",\n \"url\": \"https://ai.google.dev/gemini-api/docs/rate-limits\"\n }\n ]\n },\n {\n \"@type\": \"type.googleapis.com/google.rpc.QuotaFailure\",\n \"violations\": [\n {\n \"quotaMetric\": \"generativelanguage.googleapis.com/generate_requests_per_model_per_day\",\n \"quotaId\": \"GenerateRequestsPerDayPerProjectPerModel\",\n \"quotaDimensions\": {\n \"location\": \"global\",\n \"model\": \"gemini-2.5-pro\"\n },\n \"quotaValue | gemini-3-flash-preview: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-2.5-flash: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n", + "output_file": "", + "dockerfile_ready": false + }, + { + "tool_name": "snakemake-interface-storage-plugins", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/snakemake-interface-storage-plugins.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_snakemake-interface-storage-plugins/app/snakemake-interface-storage-plugins_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "snap-aligner", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/snap-aligner.manual_bundle.txt", + "run_help_command": false, + "status": "failed", + "error": "autogenerate_mcp_tool failed: Gemini request failed across all models. gemini-2.5-pro: HTTP 429 - {\n \"error\": {\n \"code\": 429,\n \"message\": \"You exceeded your current quota, please check your plan and billing details. For more information on this error, head to: https://ai.google.dev/gemini-api/docs/rate-limits. To monitor your current usage, head to: https://ai.dev/rate-limit. \\n* Quota exceeded for metric: generativelanguage.googleapis.com/generate_requests_per_model_per_day, limit: 1000, model: gemini-2.5-pro\\nPlease retry in 9h1m8.974756204s.\",\n \"status\": \"RESOURCE_EXHAUSTED\",\n \"details\": [\n {\n \"@type\": \"type.googleapis.com/google.rpc.Help\",\n \"links\": [\n {\n \"description\": \"Learn more about Gemini API quotas\",\n \"url\": \"https://ai.google.dev/gemini-api/docs/rate-limits\"\n }\n ]\n },\n {\n \"@type\": \"type.googleapis.com/google.rpc.QuotaFailure\",\n \"violations\": [\n {\n \"quotaMetric\": \"generativelanguage.googleapis.com/generate_requests_per_model_per_day\",\n \"quotaId\": \"GenerateRequestsPerDayPerProjectPerModel\",\n \"quotaDimensions\": {\n \"location\": \"global\",\n \"model\": \"gemini-2.5-pro\"\n },\n \"quotaValue\" | gemini-3-flash-preview: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-2.5-flash: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n", + "output_file": "", + "dockerfile_ready": false + }, + { + "tool_name": "sopa", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/sopa.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_sopa/app/sopa_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "spades", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/spades.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_spades/app/spades_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "sra-tools", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/sra-tools.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_sra-tools/app/sra-tools_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "stream", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/stream.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_stream/app/stream_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "t-coffee", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/t-coffee.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_t-coffee/app/t-coffee_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "talon", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/talon.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_talon/app/talon_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "tidyp", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/tidyp.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_tidyp/app/tidyp_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "toil", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/toil.manual_bundle.txt", + "run_help_command": false, + "status": "failed", + "error": "autogenerate_mcp_tool failed: Gemini request failed across all models. gemini-2.5-pro: HTTP 429 - {\n \"error\": {\n \"code\": 429,\n \"message\": \"You exceeded your current quota, please check your plan and billing details. For more information on this error, head to: https://ai.google.dev/gemini-api/docs/rate-limits. To monitor your current usage, head to: https://ai.dev/rate-limit. \\n* Quota exceeded for metric: generativelanguage.googleapis.com/generate_requests_per_model_per_day, limit: 1000, model: gemini-2.5-pro\\nPlease retry in 8h57m55.421701659s.\",\n \"status\": \"RESOURCE_EXHAUSTED\",\n \"details\": [\n {\n \"@type\": \"type.googleapis.com/google.rpc.Help\",\n \"links\": [\n {\n \"description\": \"Learn more about Gemini API quotas\",\n \"url\": \"https://ai.google.dev/gemini-api/docs/rate-limits\"\n }\n ]\n },\n {\n \"@type\": \"type.googleapis.com/google.rpc.QuotaFailure\",\n \"violations\": [\n {\n \"quotaMetric\": \"generativelanguage.googleapis.com/generate_requests_per_model_per_day\",\n \"quotaId\": \"GenerateRequestsPerDayPerProjectPerModel\",\n \"quotaDimensions\": {\n \"location\": \"global\",\n \"model\": \"gemini-2.5-pro\"\n },\n \"quotaValu | gemini-3-flash-preview: exception HTTPSConnectionPool(host='generativelanguage.googleapis.com', port=443): Read timed out. (read timeout=180) | gemini-2.5-flash: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n", + "output_file": "", + "dockerfile_ready": false + }, + { + "tool_name": "trim-galore", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/trim-galore.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_trim-galore/app/trim-galore_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "trinity", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/trinity.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_trinity/app/trinity_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "ucsc-cell-browser", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/ucsc-cell-browser.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_ucsc-cell-browser/app/ucsc-cell-browser_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "ucsc-nibfrag", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/ucsc-nibfrag.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_ucsc-nibfrag/app/ucsc-nibfrag_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "umi_tools", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/umi_tools.manual_bundle.txt", + "run_help_command": false, + "status": "failed", + "error": "autogenerate_mcp_tool failed: Gemini request failed across all models. gemini-2.5-pro: HTTP 429 - {\n \"error\": {\n \"code\": 429,\n \"message\": \"You exceeded your current quota, please check your plan and billing details. For more information on this error, head to: https://ai.google.dev/gemini-api/docs/rate-limits. To monitor your current usage, head to: https://ai.dev/rate-limit. \\n* Quota exceeded for metric: generativelanguage.googleapis.com/generate_requests_per_model_per_day, limit: 1000, model: gemini-2.5-pro\\nPlease retry in 8h52m17.414350332s.\",\n \"status\": \"RESOURCE_EXHAUSTED\",\n \"details\": [\n {\n \"@type\": \"type.googleapis.com/google.rpc.Help\",\n \"links\": [\n {\n \"description\": \"Learn more about Gemini API quotas\",\n \"url\": \"https://ai.google.dev/gemini-api/docs/rate-limits\"\n }\n ]\n },\n {\n \"@type\": \"type.googleapis.com/google.rpc.QuotaFailure\",\n \"violations\": [\n {\n \"quotaMetric\": \"generativelanguage.googleapis.com/generate_requests_per_model_per_day\",\n \"quotaId\": \"GenerateRequestsPerDayPerProjectPerModel\",\n \"quotaDimensions\": {\n \"model\": \"gemini-2.5-pro\",\n \"location\": \"global\"\n },\n \"quotaValu | gemini-3-flash-preview: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-2.5-flash: exception HTTPSConnectionPool(host='generativelanguage.googleapis.com', port=443): Read timed out. (read timeout=180)", + "output_file": "", + "dockerfile_ready": false + }, + { + "tool_name": "unifrac-binaries", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/unifrac-binaries.manual_bundle.txt", + "run_help_command": false, + "status": "failed", + "error": "autogenerate_mcp_tool failed: Gemini request failed across all models. gemini-2.5-pro: HTTP 429 - {\n \"error\": {\n \"code\": 429,\n \"message\": \"You exceeded your current quota, please check your plan and billing details. For more information on this error, head to: https://ai.google.dev/gemini-api/docs/rate-limits. To monitor your current usage, head to: https://ai.dev/rate-limit. \\n* Quota exceeded for metric: generativelanguage.googleapis.com/generate_requests_per_model_per_day, limit: 1000, model: gemini-2.5-pro\\nPlease retry in 8h49m16.751535117s.\",\n \"status\": \"RESOURCE_EXHAUSTED\",\n \"details\": [\n {\n \"@type\": \"type.googleapis.com/google.rpc.Help\",\n \"links\": [\n {\n \"description\": \"Learn more about Gemini API quotas\",\n \"url\": \"https://ai.google.dev/gemini-api/docs/rate-limits\"\n }\n ]\n },\n {\n \"@type\": \"type.googleapis.com/google.rpc.QuotaFailure\",\n \"violations\": [\n {\n \"quotaMetric\": \"generativelanguage.googleapis.com/generate_requests_per_model_per_day\",\n \"quotaId\": \"GenerateRequestsPerDayPerProjectPerModel\",\n \"quotaDimensions\": {\n \"model\": \"gemini-2.5-pro\",\n \"location\": \"global\"\n },\n \"quotaValu | gemini-3-flash-preview: exception HTTPSConnectionPool(host='generativelanguage.googleapis.com', port=443): Read timed out. (read timeout=180) | gemini-2.5-flash: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n", + "output_file": "", + "dockerfile_ready": false + }, + { + "tool_name": "vardict", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/vardict.manual_bundle.txt", + "run_help_command": false, + "status": "failed", + "error": "autogenerate_mcp_tool failed: Gemini request failed across all models. gemini-2.5-pro: HTTP 429 - {\n \"error\": {\n \"code\": 429,\n \"message\": \"You exceeded your current quota, please check your plan and billing details. For more information on this error, head to: https://ai.google.dev/gemini-api/docs/rate-limits. To monitor your current usage, head to: https://ai.dev/rate-limit. \\n* Quota exceeded for metric: generativelanguage.googleapis.com/generate_requests_per_model_per_day, limit: 1000, model: gemini-2.5-pro\\nPlease retry in 8h46m15.57070175s.\",\n \"status\": \"RESOURCE_EXHAUSTED\",\n \"details\": [\n {\n \"@type\": \"type.googleapis.com/google.rpc.Help\",\n \"links\": [\n {\n \"description\": \"Learn more about Gemini API quotas\",\n \"url\": \"https://ai.google.dev/gemini-api/docs/rate-limits\"\n }\n ]\n },\n {\n \"@type\": \"type.googleapis.com/google.rpc.QuotaFailure\",\n \"violations\": [\n {\n \"quotaMetric\": \"generativelanguage.googleapis.com/generate_requests_per_model_per_day\",\n \"quotaId\": \"GenerateRequestsPerDayPerProjectPerModel\",\n \"quotaDimensions\": {\n \"location\": \"global\",\n \"model\": \"gemini-2.5-pro\"\n },\n \"quotaValue | gemini-3-flash-preview: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n | gemini-2.5-flash: HTTP 400 - {\n \"error\": {\n \"code\": 400,\n \"message\": \"User location is not supported for the API use.\",\n \"status\": \"FAILED_PRECONDITION\"\n }\n}\n", + "output_file": "", + "dockerfile_ready": false + }, + { + "tool_name": "vcflib", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/vcflib.manual_bundle.txt", + "run_help_command": false, + "status": "failed", + "error": "autogenerate_mcp_tool failed: Gemini request failed across all models. gemini-2.5-pro: HTTP 429 - {\n \"error\": {\n \"code\": 429,\n \"message\": \"You exceeded your current quota, please check your plan and billing details. For more information on this error, head to: https://ai.google.dev/gemini-api/docs/rate-limits. To monitor your current usage, head to: https://ai.dev/rate-limit. \\n* Quota exceeded for metric: generativelanguage.googleapis.com/generate_requests_per_model_per_day, limit: 1000, model: gemini-2.5-pro\\nPlease retry in 8h46m14.389921185s.\",\n \"status\": \"RESOURCE_EXHAUSTED\",\n \"details\": [\n {\n \"@type\": \"type.googleapis.com/google.rpc.Help\",\n \"links\": [\n {\n \"description\": \"Learn more about Gemini API quotas\",\n \"url\": \"https://ai.google.dev/gemini-api/docs/rate-limits\"\n }\n ]\n },\n {\n \"@type\": \"type.googleapis.com/google.rpc.QuotaFailure\",\n \"violations\": [\n {\n \"quotaMetric\": \"generativelanguage.googleapis.com/generate_requests_per_model_per_day\",\n \"quotaId\": \"GenerateRequestsPerDayPerProjectPerModel\",\n \"quotaDimensions\": {\n \"location\": \"global\",\n \"model\": \"gemini-2.5-pro\"\n },\n \"quotaValu | gemini-3-flash-preview: exception HTTPSConnectionPool(host='generativelanguage.googleapis.com', port=443): Read timed out. (read timeout=180) | gemini-2.5-flash: exception HTTPSConnectionPool(host='generativelanguage.googleapis.com', port=443): Read timed out. (read timeout=180)", + "output_file": "", + "dockerfile_ready": false + }, + { + "tool_name": "viennarna", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/viennarna.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_viennarna/app/viennarna_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "vt", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/vt.manual_bundle.txt", + "run_help_command": false, + "status": "success", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_vt/app/vt_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "xclone", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/xclone.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_xclone/app/xclone_server.py", + "dockerfile_ready": true + }, + { + "tool_name": "zol", + "manual": "/225040511/project/BioScientist/agent_system/toolbase/output/help_docs/manual_bundle_txt/zol.manual_bundle.txt", + "run_help_command": false, + "status": "skipped_existing", + "error": "", + "output_file": "/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_zol/app/zol_server.py", + "dockerfile_ready": true + } +] \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_abundancebin/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_abundancebin/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..bef882fc9d87ab2e0ff61be73df1c30027f57489 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_abundancebin/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install abundancebin via conda (e.g., from bioconda) +RUN conda install -c bioconda abundancebin -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY app/abundancebin_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/abundancebin_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/abundancebin_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_abundancebin/app/abundancebin_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_abundancebin/app/abundancebin_server.py new file mode 100644 index 0000000000000000000000000000000000000000..db005e78c0b54f4f26e71ea9a203981367b36b0c --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_abundancebin/app/abundancebin_server.py @@ -0,0 +1,134 @@ +import subprocess +import tempfile +from pathlib import Path +from typing import Optional, List + +# This is a placeholder for the MCP decorator. +# In a real MCP environment, this would be imported from the MCP library. +def tool(*args, **kwargs): + def decorator(f): + return f + return decorator + +mcp = type("mcp", (), {"tool": tool}) + + +@mcp.tool() +def abundancebin( + input_file: Path, + kmer_len: int = 20, + output: Optional[Path] = None, + exclude: Optional[int] = None, + exclude_max: Optional[int] = None, + output_fasta: bool = False, + bin_num: Optional[int] = None, + recursive_classification: bool = False, +): + """ + Bins sequences based on k-mer composition and abundance. + + This tool can either classify sequences into a predefined number of bins + or use a recursive classification approach. + + Args: + input_file: Path to the input filename. + kmer_len: Composition length (k-mer size). Defaults to 20. + output: Path to the output file. Defaults to inputfile.log. + exclude: Exclude sequences with a count below this value. + exclude_max: Exclude sequences with a count above this value. + output_fasta: If set, output results in FASTA format. + bin_num: The number of bins to classify sequences into. + recursive_classification: If set, undergo recursive classification. + Cannot be used with bin_num. + + Returns: + A dictionary containing the command executed, stdout, stderr, and a list of output files. + """ + # --- Input Validation --- + if not input_file.is_file(): + raise FileNotFoundError(f"Input file not found: {input_file}") + + if kmer_len <= 0: + raise ValueError("kmer_len must be a positive integer.") + + if exclude is not None and exclude < 0: + raise ValueError("The 'exclude' count must be a non-negative integer.") + + if exclude_max is not None and exclude_max < 0: + raise ValueError("The 'exclude_max' count must be a non-negative integer.") + + if bin_num is not None and bin_num <= 0: + raise ValueError("The 'bin_num' must be a positive integer.") + + if bin_num is not None and recursive_classification: + raise ValueError( + "Cannot specify both 'bin_num' and 'recursive_classification'. " + "Please choose one method for classification." + ) + + # --- Command Construction --- + cmd = ["abundancebin"] + + cmd.extend(["-input", str(input_file)]) + + # Handle default output file path + if output: + output_path = output + else: + output_path = input_file.with_suffix('.log') + + cmd.extend(["-output", str(output_path)]) + + cmd.extend(["-kmer_len", str(kmer_len)]) + + if exclude is not None: + cmd.extend(["-exclude", str(exclude)]) + + if exclude_max is not None: + cmd.extend(["-exclude_max", str(exclude_max)]) + + if output_fasta: + cmd.append("-OUTPUT_FASTA") + + if bin_num is not None: + cmd.extend(["-bin_num", str(bin_num)]) + + if recursive_classification: + cmd.append("-RECURSIVE_CLASSIFICATION") + + command_executed = " ".join(cmd) + + # --- Subprocess Execution --- + try: + result = subprocess.run( + cmd, + capture_output=True, + text=True, + check=True + ) + stdout = result.stdout + stderr = result.stderr + except FileNotFoundError: + return { + "command_executed": command_executed, + "stdout": "", + "stderr": "Error: 'abundancebin' command not found. Make sure it is installed and in your PATH.", + "output_files": [] + } + except subprocess.CalledProcessError as e: + return { + "command_executed": command_executed, + "stdout": e.stdout, + "stderr": e.stderr, + "output_files": [] + } + + # --- Structured Result Return --- + output_files = [str(output_path)] if output_path.exists() else [] + + return { + "command_executed": command_executed, + "stdout": stdout, + "stderr": stderr, + "output_files": output_files + } \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_abundancebin/app/abundancebin_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_abundancebin/app/abundancebin_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..025ddd800f4e711f69e41e76e41f16d4f7adb0b4 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_abundancebin/app/abundancebin_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_abundancebin/app/abundancebin_server.py') +SERVER_NAME = 'biosci_abundancebin' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_abundancebin/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_abundancebin/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_abundancebin/app/requirements.txt @@ -0,0 +1 @@ + diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_abundancebin/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_abundancebin/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..c3e728f0802fb0284f48f87c588f826b60f324c2 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_abundancebin/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-abundancebin: + build: . + image: mcp-abundancebin:latest + container_name: mcp-abundancebin + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=abundancebin + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_abundancebin/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_abundancebin/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..fb3f2d4c33b5cfc882a79148b0d58be0b05b89bc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_abundancebin/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - abundancebin + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_abundancebin/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_abundancebin/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_abundancebin/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_anarci/app/anarci_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_anarci/app/anarci_server.py new file mode 100644 index 0000000000000000000000000000000000000000..236664764dd6dfa473b7a43786f9336c963623c7 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_anarci/app/anarci_server.py @@ -0,0 +1,183 @@ +from pathlib import Path +import subprocess +from typing import Optional, List, Union + +@mcp.tool() +def run_anarci( + sequence: Optional[str] = None, + input_file: Optional[str] = None, + output_file: Optional[str] = None, + scheme: str = "imgt", + restrict: Optional[str] = None, + species: Optional[str] = None, + csv: bool = False, + ncpu: int = 1, + bit_score_threshold: float = 80.0, + assign_germline: bool = False, + name: Optional[str] = None, + verbose: bool = False +): + """ + ANARCI: Antibody Numbering and Antigen Receptor ClassIfication. + Numbers amino-acid sequences of antibody and T-cell receptor variable domains. + + Args: + sequence: A single amino acid sequence to number. + input_file: Path to a FASTA file containing sequences to number. + output_file: Path to the output file. If not provided, results are printed to stdout. + scheme: Numbering scheme to use. Options: 'imgt', 'kabat', 'chothia', 'martin', 'aho'. (Default: 'imgt') + restrict: Restrict numbering to specific domain types. Options: 'ig' (immunoglobulins), 'tr' (T-cell receptors). + species: Restrict germline search to specific species (e.g., 'human', 'mouse', 'rat', 'rabbit', 'rhesus', 'pig', 'alpaca'). + csv: If True, output results in CSV format (horizontal). If False, uses plain text (vertical). + ncpu: Number of CPUs to use for parallel processing. (Default: 1) + bit_score_threshold: HMMER bit score threshold for domain identification. (Default: 80.0) + assign_germline: If True, assign germline species and gene information. + name: Name to assign to the single sequence (used with 'sequence' parameter). + verbose: If True, produce verbose output. + """ + + # Input validation + if not sequence and not input_file: + raise ValueError("Either 'sequence' or 'input_file' must be provided.") + + if sequence and input_file: + raise ValueError("Provide either 'sequence' or 'input_file', not both.") + + valid_schemes = ["imgt", "kabat", "chothia", "martin", "aho"] + if scheme.lower() not in valid_schemes: + raise ValueError(f"Invalid scheme '{scheme}'. Must be one of {valid_schemes}") + + if restrict and restrict.lower() not in ["ig", "tr"]: + raise ValueError("Restrict must be either 'ig' or 'tr'.") + + # Build command + # The CLI tool is typically named 'ANARCI' (uppercase) + cmd = ["ANARCI"] + + if sequence: + cmd.extend(["--sequence", sequence]) + if name: + cmd.extend(["--name", name]) + + if input_file: + input_path = Path(input_file) + if not input_path.exists(): + raise FileNotFoundError(f"Input file not found: {input_file}") + cmd.extend(["--input", str(input_path)]) + + if output_file: + cmd.extend(["--outfile", output_file]) + + cmd.extend(["--scheme", scheme.lower()]) + + if restrict: + cmd.extend(["--restrict", restrict.lower()]) + + if species: + cmd.extend(["--species", species.lower()]) + + if csv: + cmd.append("--csv") + + if assign_germline: + cmd.append("--assign_germline") + + if verbose: + cmd.append("--verbose") + + cmd.extend(["--ncpu", str(ncpu)]) + cmd.extend(["--bit_score_threshold", str(bit_score_threshold)]) + + try: + # Execute the tool + result = subprocess.run( + cmd, + capture_output=True, + text=True, + check=True + ) + + output_files = [] + if output_file: + # ANARCI often appends suffixes depending on the format + # e.g., .csv or .txt + actual_output = Path(output_file) + if actual_output.exists(): + output_files.append(str(actual_output)) + else: + # Check for common suffixes if the exact file isn't found + for suffix in [".csv", ".txt"]: + p = Path(f"{output_file}{suffix}") + if p.exists(): + output_files.append(str(p)) + + return { + "command_executed": " ".join(cmd), + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": output_files + } + + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(cmd), + "stdout": e.stdout, + "stderr": e.stderr, + "error": f"ANARCI failed with return code {e.returncode}" + } + except Exception as e: + return { + "command_executed": " ".join(cmd), + "error": str(e) + } + +@mcp.tool() +def list_anarci_species(): + """ + Returns a list of species supported by ANARCI for germline alignment. + Note: This is an informational tool based on ANARCI's standard database. + """ + # Standard species supported by ANARCI + species_list = [ + "human", "mouse", "rat", "rabbit", "rhesus", "pig", "alpaca", + "dog", "chicken", "cow", "horse", "monkey", "macaque" + ] + + return { + "supported_species": species_list, + "note": "Availability may depend on the specific ANARCI version and installed HMM databases." + } + +@mcp.tool() +def get_anarci_version(): + """ + Retrieves the version information for the installed ANARCI tool. + """ + try: + # ANARCI doesn't always have a simple --version flag in older versions, + # but we can try to run it to see the help header or use pip/conda info. + # Most modern versions support --version. + result = subprocess.run( + ["ANARCI", "--version"], + capture_output=True, + text=True, + check=False + ) + + # If --version fails, try to get it from the help output + if result.returncode != 0: + result = subprocess.run( + ["ANARCI", "--help"], + capture_output=True, + text=True, + check=False + ) + + return { + "stdout": result.stdout, + "stderr": result.stderr + } + except Exception as e: + return { + "error": f"Could not determine ANARCI version: {str(e)}" + } \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_anarci/app/anarci_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_anarci/app/anarci_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..55ff8f0288ce249db3018b5ca2ac023e08892dfc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_anarci/app/anarci_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_anarci/app/anarci_server.py') +SERVER_NAME = 'biosci_anarci' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_anarci/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_anarci/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..74cc2ae89329fd2e72df0e4d5ded04a73743f2be --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_anarci/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-anarci: + build: . + image: mcp-anarci:latest + container_name: mcp-anarci + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=anarci + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_anarci/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_anarci/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..68546623ce299a013b727b854f595fddd6b9586e --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_anarci/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - anarci + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_ariba/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_ariba/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..64f32321bcd1440c4b47360773aa12ac582f949e --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_ariba/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install ariba via conda (e.g., from bioconda) +RUN conda install -c bioconda ariba -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY app/ariba_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/ariba_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/ariba_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_ariba/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_ariba/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_ariba/app/requirements.txt @@ -0,0 +1 @@ + diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_ariba/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_ariba/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..3fe30327b9341b624c6029e37d388c193221cbca --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_ariba/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-ariba: + build: . + image: mcp-ariba:latest + container_name: mcp-ariba + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=ariba + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_ariba/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_ariba/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..774ae8821bdba1c2e80cce68add26a6a867b0ce5 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_ariba/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - ariba + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_ariba/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_ariba/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_ariba/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-alabaster.spatial/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-alabaster.spatial/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..40770fc4cf44910bbe4a77f31ad3f18c226c857b --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-alabaster.spatial/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install bioconductor-alabaster.spatial via conda (e.g., from bioconda) +RUN conda install -c bioconda bioconductor-alabaster.spatial -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY app/bioconductor-alabaster.spatial_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/bioconductor-alabaster.spatial_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/bioconductor-alabaster.spatial_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-alabaster.spatial/app/bioconductor-alabaster.spatial_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-alabaster.spatial/app/bioconductor-alabaster.spatial_server.py new file mode 100644 index 0000000000000000000000000000000000000000..0a106270b1f6bae5b7a4069bdc49142f726df900 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-alabaster.spatial/app/bioconductor-alabaster.spatial_server.py @@ -0,0 +1,289 @@ +import subprocess +import tempfile +import os +from pathlib import Path +from typing import Optional, List, Dict, Any + +# No need to import mcp as per instructions. + +def _run_r_script(script_content: str, cwd: Path) -> Dict[str, Any]: + """ + Executes an R script and captures its output. + + Args: + script_content: The R script as a string. + cwd: The current working directory for the R script execution. + + Returns: + A dictionary containing execution details, including command, stdout, stderr, + and an error message if the script fails. + """ + with tempfile.NamedTemporaryFile(mode="w", suffix=".R", delete=False) as r_script_file: + r_script_file.write(script_content) + r_script_path = Path(r_script_file.name) + + command = ["Rscript", str(r_script_path)] + + stdout = "" + stderr = "" + try: + process = subprocess.run( + command, + capture_output=True, + text=True, + check=True, + cwd=cwd # Run R script in the specified working directory + ) + stdout = process.stdout + stderr = process.stderr + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(command), + "stdout": e.stdout, + "stderr": e.stderr, + "error": f"R script execution failed with exit code {e.returncode}", + "output_files": [], + } + finally: + os.remove(r_script_path) + + return { + "command_executed": " ".join(command), + "stdout": stdout, + "stderr": stderr, + "output_files": [], # This will be populated by the calling function + } + +@mcp.tool() +def save_spatial_experiment( + input_spatial_experiment_rds: Path, + output_directory: Path, + precomputed: bool = True, + deferred: bool = False, + compression_level: Optional[int] = None, + compression_filter: Optional[str] = None, +) -> Dict[str, Any]: + """ + Saves a SpatialExperiment object and its images into file artifacts. + + This function serializes an R SpatialExperiment object (provided as an RDS file) + into a directory structure of file artifacts, offering a portable alternative + to direct RDS serialization. The `alabaster.spatial` package handles the + conversion and storage of the object and its associated images. + + Args: + input_spatial_experiment_rds: Path to an RDS file containing the SpatialExperiment object. + This file must exist. + output_directory: Path to the directory where the file artifacts will be saved. + This directory will be created if it does not exist. + precomputed: Logical, whether to save precomputed results. Defaults to TRUE. + deferred: Logical, whether to defer saving of certain components. Defaults to FALSE. + compression_level: Integer, compression level for HDF5 files (0-9). + If None, R's default (typically 6) will be used. + compression_filter: String, compression filter for HDF5 files (e.g., "gzip", "lzf"). + If None, no specific filter is applied. + + Returns: + A dictionary containing execution details, including the command executed, + stdout, stderr, and a list of generated output files/directories. + Returns an error if input file is not found, output path is invalid, + compression_level is out of range, or R script execution fails. + """ + # Input validation + if not input_spatial_experiment_rds.is_file(): + return { + "command_executed": "", + "stdout": "", + "stderr": f"Error: Input RDS file not found at '{input_spatial_experiment_rds}'", + "output_files": [], + } + + if output_directory.exists() and not output_directory.is_dir(): + return { + "command_executed": "", + "stdout": "", + "stderr": f"Error: Output path '{output_directory}' exists but is not a directory.", + "output_files": [], + } + + try: + output_directory.mkdir(parents=True, exist_ok=True) + except OSError as e: + return { + "command_executed": "", + "stdout": "", + "stderr": f"Error: Could not create output directory '{output_directory}': {e}", + "output_files": [], + } + + if compression_level is not None and not (0 <= compression_level <= 9): + return { + "command_executed": "", + "stdout": "", + "stderr": f"Error: 'compression_level' must be an integer between 0 and 9, got {compression_level}", + "output_files": [], + } + + # Construct R script + r_script_template = """ + suppressPackageStartupMessages({{ + library(alabaster.spatial) + library(SpatialExperiment) + library(S4Vectors) + library(alabaster.base) + library(rhdf5) # Explicitly load for HDF5 operations + }}) + + input_rds_path <- "{input_rds_path}" + output_dir_path <- "{output_dir_path}" + + # Load the SpatialExperiment object + if (!file.exists(input_rds_path)) {{ + stop(paste0("Input RDS file not found: ", input_rds_path)) + }} + se_object <- readRDS(input_rds_path) + + # Ensure it's a SpatialExperiment object + if (!is(se_object, "SpatialExperiment")) {{ + stop("Loaded object is not a SpatialExperiment object.") + }} + + # Prepare additional arguments for saveObject + save_args <- list( + x = se_object, + path = output_dir_path, + precomputed = {precomputed_r}, + deferred = {deferred_r} + ) + {compression_level_r} + {compression_filter_r} + + # Call saveObject + do.call(alabaster.spatial::saveObject, save_args) + + message(paste0("SpatialExperiment object saved to: ", output_dir_path)) + """ + + compression_level_r = f"save_args$compression_level <- {compression_level}" if compression_level is not None else "" + compression_filter_r = f"save_args$compression_filter <- '{compression_filter}'" if compression_filter is not None else "" + + r_script_content = r_script_template.format( + input_rds_path=input_spatial_experiment_rds.resolve(), + output_dir_path=output_directory.resolve(), + precomputed_r=str(precomputed).upper(), + deferred_r=str(deferred).upper(), + compression_level_r=compression_level_r, + compression_filter_r=compression_filter_r, + ) + + # Execute R script + result = _run_r_script(r_script_content, output_directory.parent) # Run in parent dir to allow output_directory creation + if "error" in result: + return result + + result["output_files"] = [str(output_directory.resolve())] + return result + + +@mcp.tool() +def read_spatial_experiment( + input_directory: Path, + output_spatial_experiment_rds: Path, + metadata_only: bool = False, + image_format: Optional[str] = None, +) -> Dict[str, Any]: + """ + Loads a SpatialExperiment object and its images from file artifacts. + + This function reconstructs an R SpatialExperiment object from a directory + of file artifacts previously saved by `save_spatial_experiment`. + + Args: + input_directory: Path to the directory containing the file artifacts. + This directory must exist and be valid. + output_spatial_experiment_rds: Path to the output RDS file where the + reconstructed SpatialExperiment object will be saved. + The parent directory will be created if it does not exist. + metadata_only: Logical, whether to only load metadata and not the full data. + Defaults to FALSE. + image_format: String, desired image format for loading (e.g., "png", "jpeg", "tiff"). + If None, images are loaded in their original format. + + Returns: + A dictionary containing execution details, including the command executed, + stdout, stderr, and a list of generated output files. + Returns an error if input directory is not found, output path is invalid, + or R script execution fails. + """ + # Input validation + if not input_directory.is_dir(): + return { + "command_executed": "", + "stdout": "", + "stderr": f"Error: Input directory not found at '{input_directory}'", + "output_files": [], + } + + if output_spatial_experiment_rds.exists() and not output_spatial_experiment_rds.is_file(): + return { + "command_executed": "", + "stdout": "", + "stderr": f"Error: Output path '{output_spatial_experiment_rds}' exists but is not a file.", + "output_files": [], + } + + try: + output_spatial_experiment_rds.parent.mkdir(parents=True, exist_ok=True) + except OSError as e: + return { + "command_executed": "", + "stdout": "", + "stderr": f"Error: Could not create parent directory for output RDS file '{output_spatial_experiment_rds}': {e}", + "output_files": [], + } + + # Construct R script + r_script_template = """ + suppressPackageStartupMessages({{ + library(alabaster.spatial) + library(SpatialExperiment) + library(S4Vectors) + library(alabaster.base) + library(rhdf5) # Explicitly load for HDF5 operations + }}) + + input_dir_path <- "{input_dir_path}" + output_rds_path <- "{output_rds_path}" + + # Prepare additional arguments for readObject + read_args <- list( + path = input_dir_path, + metadata.only = {metadata_only_r} + ) + {image_format_r} + + # Call readObject + se_object <- do.call(alabaster.spatial::readObject, read_args) + + # Save the SpatialExperiment object to RDS + saveRDS(se_object, file = output_rds_path) + + message(paste0("SpatialExperiment object loaded from: ", input_dir_path, " and saved to: ", output_rds_path)) + """ + + image_format_r = f"read_args$image.format <- '{image_format}'" if image_format is not None else "" + + r_script_content = r_script_template.format( + input_dir_path=input_directory.resolve(), + output_rds_path=output_spatial_experiment_rds.resolve(), + metadata_only_r=str(metadata_only).upper(), + image_format_r=image_format_r, + ) + + # Execute R script + result = _run_r_script(r_script_content, output_spatial_experiment_rds.parent) + if "error" in result: + return result + + result["output_files"] = [str(output_spatial_experiment_rds.resolve())] + return result \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-alabaster.spatial/app/bioconductor-alabaster.spatial_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-alabaster.spatial/app/bioconductor-alabaster.spatial_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..63c86c9addbd69b02c55f1933afb50eff359707c --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-alabaster.spatial/app/bioconductor-alabaster.spatial_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-alabaster.spatial/app/bioconductor-alabaster.spatial_server.py') +SERVER_NAME = 'biosci_bioconductor_alabaster_spatial' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-alabaster.spatial/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-alabaster.spatial/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..9077879ad1f81983e4ba6ace465ba860f645b5be --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-alabaster.spatial/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-bioconductor-alabaster.spatial: + build: . + image: mcp-bioconductor-alabaster.spatial:latest + container_name: mcp-bioconductor-alabaster.spatial + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=bioconductor-alabaster.spatial + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-alabaster.spatial/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-alabaster.spatial/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..329b888afd15efdc23d52fc58fe6ff2376c71013 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-alabaster.spatial/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - bioconductor-alabaster.spatial + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-alabaster.spatial/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-alabaster.spatial/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-alabaster.spatial/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-annotationhub/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-annotationhub/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-annotationhub/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-awaggregator/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-awaggregator/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..53b52deea05f9bf6f66ad8a3024b5e2a18923bb4 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-awaggregator/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install bioconductor-awaggregator via conda (e.g., from bioconda) +RUN conda install -c bioconda bioconductor-awaggregator -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY app/bioconductor-awaggregator_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/bioconductor-awaggregator_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/bioconductor-awaggregator_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-awaggregator/app/bioconductor-awaggregator_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-awaggregator/app/bioconductor-awaggregator_server.py new file mode 100644 index 0000000000000000000000000000000000000000..2c712046b6babe4f14d49283b84338eb72719930 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-awaggregator/app/bioconductor-awaggregator_server.py @@ -0,0 +1,214 @@ +import subprocess +from pathlib import Path +from typing import Dict, List + +# MCP decorator is assumed to be available in the execution environment. +# No import is needed as per the instructions. +# class mcp: +# @staticmethod +# def tool(): +# def decorator(func): +# return func +# return decorator + + +def _run_awaggregator_cli(cmd: List[str], output_map: Dict[str, Path]) -> Dict: + """ + A private helper function to execute the awaggregator wrapper script, + handle subprocess execution, and format the output. + """ + command_executed = " ".join(map(str, cmd)) + try: + result = subprocess.run( + cmd, + capture_output=True, + text=True, + check=True, + ) + return { + "command_executed": command_executed, + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": {key: str(path) for key, path in output_map.items()}, + } + except FileNotFoundError: + # This error is raised if 'Rscript' or the wrapper is not in PATH + return { + "command_executed": command_executed, + "stdout": "", + "stderr": "Error: 'Rscript' or the wrapper script 'run_awaggregator.R' not found. " + "Please ensure R and the tool's wrapper script are in the system's PATH.", + "output_files": {}, + "error": "Executable not found." + } + except subprocess.CalledProcessError as e: + # This error is raised for non-zero exit codes from the tool + return { + "command_executed": command_executed, + "stdout": e.stdout, + "stderr": e.stderr, + "output_files": {}, + "error": f"Tool execution failed with exit code {e.returncode}", + } + + +@mcp.tool() +def aw_weight( + expression_matrix: Path, + design_matrix: Path, + output_weights_file: Path, +): + """ + Computes adaptive weights for gene expression data meta-analysis. + + This tool wraps the `aw.weight` function from the Bioconductor `awaggregator` package. + It takes an expression data matrix and a design matrix to compute weights for each study. + The resulting weights matrix can be used with the `aw_mean` or `aw_median` tools. + + Args: + expression_matrix: Path to the input expression data matrix file (e.g., CSV, TSV). + Rows should represent genes and columns should represent samples. + design_matrix: Path to the input design matrix file (e.g., CSV, TSV). + This matrix describes the experimental design. + output_weights_file: Path to save the computed weights matrix. + + Returns: + A dictionary containing the execution command, stdout, stderr, and a + path to the output weights file. + """ + # --- Input Validation --- + if not expression_matrix.is_file(): + raise FileNotFoundError(f"Input expression matrix file not found: {expression_matrix}") + if not design_matrix.is_file(): + raise FileNotFoundError(f"Input design matrix file not found: {design_matrix}") + + output_dir = output_weights_file.parent + if not output_dir.is_dir(): + raise NotADirectoryError(f"Output directory does not exist: {output_dir}") + + # --- Command Construction --- + # Assumes a wrapper script 'run_awaggregator.R' is available that handles CLI parsing. + cmd = [ + "Rscript", + "run_awaggregator.R", + "--method", + "weight", + "--expression", + expression_matrix, + "--design", + design_matrix, + "--output", + output_weights_file, + ] + + return _run_awaggregator_cli(cmd, {"weights_matrix": output_weights_file}) + + +@mcp.tool() +def aw_mean( + data_matrix: Path, + weights_matrix: Path, + output_file: Path, + na_rm: bool = False, +): + """ + Performs adaptive-weighted mean aggregation on gene expression data. + + This tool wraps the `aw.mean` function from the Bioconductor `awaggregator` package. + It takes a data matrix and a corresponding weights matrix to compute the weighted mean + for each gene (row) across studies (columns). + + Args: + data_matrix: Path to the input data matrix file (e.g., CSV, TSV). + Rows should represent genes and columns should represent studies. + weights_matrix: Path to the input weights matrix file (e.g., CSV, TSV). + Must have the same dimensions as the data matrix. + output_file: Path to save the aggregated results. + na_rm: If True, NA values will be removed before computation. Defaults to False. + + Returns: + A dictionary containing the execution command, stdout, stderr, and a + path to the output file. + """ + # --- Input Validation --- + if not data_matrix.is_file(): + raise FileNotFoundError(f"Input data matrix file not found: {data_matrix}") + if not weights_matrix.is_file(): + raise FileNotFoundError(f"Input weights matrix file not found: {weights_matrix}") + + output_dir = output_file.parent + if not output_dir.is_dir(): + raise NotADirectoryError(f"Output directory does not exist: {output_dir}") + + # --- Command Construction --- + cmd = [ + "Rscript", + "run_awaggregator.R", + "--method", + "mean", + "--data", + data_matrix, + "--weights", + weights_matrix, + "--output", + output_file, + ] + if na_rm: + cmd.append("--na-rm") + + return _run_awaggregator_cli(cmd, {"aggregated_data": output_file}) + + +@mcp.tool() +def aw_median( + data_matrix: Path, + weights_matrix: Path, + output_file: Path, + na_rm: bool = False, +): + """ + Performs adaptive-weighted median aggregation on gene expression data. + + This tool wraps the `aw.median` function from the Bioconductor `awaggregator` package. + It takes a data matrix and a corresponding weights matrix to compute the weighted median + for each gene (row) across studies (columns). + + Args: + data_matrix: Path to the input data matrix file (e.g., CSV, TSV). + Rows should represent genes and columns should represent studies. + weights_matrix: Path to the input weights matrix file (e.g., CSV, TSV). + Must have the same dimensions as the data matrix. + output_file: Path to save the aggregated results. + na_rm: If True, NA values will be removed before computation. Defaults to False. + + Returns: + A dictionary containing the execution command, stdout, stderr, and a + path to the output file. + """ + # --- Input Validation --- + if not data_matrix.is_file(): + raise FileNotFoundError(f"Input data matrix file not found: {data_matrix}") + if not weights_matrix.is_file(): + raise FileNotFoundError(f"Input weights matrix file not found: {weights_matrix}") + + output_dir = output_file.parent + if not output_dir.is_dir(): + raise NotADirectoryError(f"Output directory does not exist: {output_dir}") + + # --- Command Construction --- + cmd = [ + "Rscript", + "run_awaggregator.R", + "--method", + "median", + "--data", + data_matrix, + "--weights", + weights_matrix, + "--output", + output_file, + ] + if na_rm: + cmd.append("--na-rm") + + return _run_awaggregator_cli(cmd, {"aggregated_data": output_file}) \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-awaggregator/app/bioconductor-awaggregator_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-awaggregator/app/bioconductor-awaggregator_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..03fc0c555824375e2e1491f0c579ecce8ee4f777 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-awaggregator/app/bioconductor-awaggregator_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-awaggregator/app/bioconductor-awaggregator_server.py') +SERVER_NAME = 'biosci_bioconductor_awaggregator' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-awaggregator/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-awaggregator/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-awaggregator/app/requirements.txt @@ -0,0 +1 @@ + diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-awaggregator/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-awaggregator/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..ff0371023c533e11773174f48bd51a6d9216bff9 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-awaggregator/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-bioconductor-awaggregator: + build: . + image: mcp-bioconductor-awaggregator:latest + container_name: mcp-bioconductor-awaggregator + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=bioconductor-awaggregator + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-awaggregator/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-awaggregator/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..4a35b9239801536a5473e94323a22b826418478d --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-awaggregator/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - bioconductor-awaggregator + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-awaggregator/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-awaggregator/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-awaggregator/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-bsgenome/app/bioconductor-bsgenome_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-bsgenome/app/bioconductor-bsgenome_server.py new file mode 100644 index 0000000000000000000000000000000000000000..7e1a167c0a157f9f23cca698d11b56020f0b2dfd --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-bsgenome/app/bioconductor-bsgenome_server.py @@ -0,0 +1,270 @@ +import subprocess +from pathlib import Path +from typing import Optional, List, Dict, Any + +# No need to import mcp as per instructions. + +def _run_r_command(r_code: str) -> Dict[str, Any]: + """ + Executes an R command using Rscript. + + Args: + r_code: The R code string to execute. + + Returns: + A dictionary containing command execution details, stdout, stderr, and any output files. + """ + command = ["Rscript", "-e", r_code] + try: + process = subprocess.run( + command, + capture_output=True, + text=True, + check=True + ) + return { + "command_executed": " ".join(command), + "stdout": process.stdout, + "stderr": process.stderr, + "output_files": [], + } + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(command), + "stdout": e.stdout, + "stderr": e.stderr, + "error": str(e), + "output_files": [], + } + except FileNotFoundError: + return { + "command_executed": " ".join(command), + "stdout": "", + "stderr": "Error: Rscript command not found. Please ensure R is installed and in your PATH.", + "error": "Rscript not found", + "output_files": [], + } + +@mcp.tool() +def install_bsgenome_base_package( + bioc_manager_repo: str = "https://bioconductor.org/packages/release/bioc", +) -> Dict[str, Any]: + """ + Installs the core 'BSgenome' R package and 'BiocManager' if not already present. + + This function is a prerequisite for using BSgenome data packages or functionalities. + It ensures that the necessary R infrastructure is in place. + + Args: + bioc_manager_repo: The Bioconductor repository URL to use for installation. + Defaults to the standard release repository. + + Returns: + A dictionary containing command execution details, stdout, stderr, and any output files. + """ + # Input validation + if not isinstance(bioc_manager_repo, str) or not bioc_manager_repo: + raise ValueError("Bioconductor repository URL must be a non-empty string.") + + r_code = f""" + if (!require("BiocManager", quietly = TRUE)) {{ + install.packages("BiocManager", repos="https://cloud.r-project.org") + }} + BiocManager::install("BSgenome", update=FALSE, ask=FALSE, siteRepos="{bioc_manager_repo}") + """ + return _run_r_command(r_code) + +@mcp.tool() +def install_bsgenome_data_package( + data_package_name: str, + bioc_manager_repo: str = "https://bioconductor.org/packages/release/bioc", +) -> Dict[str, Any]: + """ + Installs a specific BSgenome data package (e.g., 'BSgenome.Hsapiens.UCSC.hg38'). + + These data packages contain the actual genome sequences. The core 'BSgenome' + package must be installed first using `install_bsgenome_base_package`. + + Args: + data_package_name: The exact name of the BSgenome data package to install + (e.g., "BSgenome.Hsapiens.UCSC.hg38"). + bioc_manager_repo: The Bioconductor repository URL to use for installation. + Defaults to the standard release repository. + + Returns: + A dictionary containing command execution details, stdout, stderr, and any output files. + """ + # Input validation + if not isinstance(data_package_name, str) or not data_package_name: + raise ValueError("Data package name must be a non-empty string.") + if not data_package_name.startswith("BSgenome."): + raise ValueError(f"Invalid BSgenome data package name: '{data_package_name}'. " + "It should start with 'BSgenome.'.") + if not isinstance(bioc_manager_repo, str) or not bioc_manager_repo: + raise ValueError("Bioconductor repository URL must be a non-empty string.") + + r_code = f""" + if (!require("BiocManager", quietly = TRUE)) {{ + install.packages("BiocManager", repos="https://cloud.r-project.org") + }} + BiocManager::install("{data_package_name}", update=FALSE, ask=FALSE, siteRepos="{bioc_manager_repo}") + """ + return _run_r_command(r_code) + +@mcp.tool() +def get_genome_sequence( + data_package_name: str, + seqname: str, + start: int, + end: int, + masked: bool = False, + output_file: Optional[Path] = None, +) -> Dict[str, Any]: + """ + Retrieves a specific genomic sequence from an installed BSgenome data package. + + The specified BSgenome data package must be installed prior to calling this function + using `install_bsgenome_data_package`. + + Args: + data_package_name: The name of the installed BSgenome data package + (e.g., "BSgenome.Hsapiens.UCSC.hg38"). + seqname: The name of the sequence (e.g., "chr1", "X"). + start: The 1-based start coordinate of the sequence to retrieve. Must be a positive integer. + end: The 1-based end coordinate of the sequence to retrieve. Must be an integer + greater than or equal to start. + masked: If True, masked regions (e.g., repeats) will be returned as 'N's. + If False, the actual sequence will be returned. Defaults to False. + output_file: Optional path to a file where the retrieved sequence will be written. + If not provided, the sequence will be returned in stdout. + + Returns: + A dictionary containing command execution details, stdout, stderr, and any output files. + """ + # Input validation + if not isinstance(data_package_name, str) or not data_package_name: + raise ValueError("Data package name must be a non-empty string.") + if not data_package_name.startswith("BSgenome."): + raise ValueError(f"Invalid BSgenome data package name: '{data_package_name}'. " + "It should start with 'BSgenome.'.") + if not isinstance(seqname, str) or not seqname: + raise ValueError("Sequence name (seqname) must be a non-empty string.") + if not isinstance(start, int) or start <= 0: + raise ValueError("Start coordinate must be a positive integer.") + if not isinstance(end, int) or end < start: + raise ValueError("End coordinate must be an integer greater than or equal to the start coordinate.") + if not isinstance(masked, bool): + raise ValueError("Masked parameter must be a boolean.") + if output_file is not None and not isinstance(output_file, Path): + raise ValueError("Output file must be a Path object or None.") + + # Prepare R code + r_masked_val = "TRUE" if masked else "FALSE" + + if output_file: + output_file_path = Path(output_file) + # Ensure parent directory exists + output_file_path.parent.mkdir(parents=True, exist_ok=True) + + # Modify R code to write to file + r_code = f""" + library({data_package_name}) + genome_obj <- get(data_package_name) + seq <- getSeq(genome_obj, "{seqname}", start={start}, end={end}, masked={r_masked_val}) + writeLines(as.character(seq), con="{output_file_path.resolve()}") + """ + result = _run_r_command(r_code) + if "error" not in result: + result["output_files"].append(str(output_file_path.resolve())) + return result + else: + r_code = f""" + library({data_package_name}) + genome_obj <- get(data_package_name) + seq <- getSeq(genome_obj, "{seqname}", start={start}, end={end}, masked={r_masked_val}) + cat(as.character(seq)) + """ + return _run_r_command(r_code) + +@mcp.tool() +def list_available_sequences( + data_package_name: str, +) -> Dict[str, Any]: + """ + Lists the available sequence names (e.g., chromosomes) within an installed BSgenome data package. + + The specified BSgenome data package must be installed prior to calling this function + using `install_bsgenome_data_package`. + + Args: + data_package_name: The name of the installed BSgenome data package + (e.g., "BSgenome.Hsapiens.UCSC.hg38"). + + Returns: + A dictionary containing command execution details, stdout, stderr, and any output files. + The stdout will contain a newline-separated list of sequence names. + """ + # Input validation + if not isinstance(data_package_name, str) or not data_package_name: + raise ValueError("Data package name must be a non-empty string.") + if not data_package_name.startswith("BSgenome."): + raise ValueError(f"Invalid BSgenome data package name: '{data_package_name}'. " + "It should start with 'BSgenome.'.") + + r_code = f""" + library({data_package_name}) + genome_obj <- get(data_package_name) + cat(seqnames(genome_obj), sep="\\n") + """ + return _run_r_command(r_code) + +@mcp.tool() +def get_sequence_lengths( + data_package_name: str, + seqname: Optional[str] = None, +) -> Dict[str, Any]: + """ + Retrieves the lengths of sequences (e.g., chromosomes) within an installed BSgenome data package. + + The specified BSgenome data package must be installed prior to calling this function + using `install_bsgenome_data_package`. + + Args: + data_package_name: The name of the installed BSgenome data package + (e.g., "BSgenome.Hsapiens.UCSC.hg38"). + seqname: Optional. If provided, returns the length of only this specific sequence. + If None, returns lengths for all sequences. + + Returns: + A dictionary containing command execution details, stdout, stderr, and any output files. + The stdout will contain sequence names and their lengths, typically one per line. + """ + # Input validation + if not isinstance(data_package_name, str) or not data_package_name: + raise ValueError("Data package name must be a non-empty string.") + if not data_package_name.startswith("BSgenome."): + raise ValueError(f"Invalid BSgenome data package name: '{data_package_name}'. " + "It should start with 'BSgenome.'.") + if seqname is not None and (not isinstance(seqname, str) or not seqname): + raise ValueError("Sequence name (seqname) must be a non-empty string if provided.") + + if seqname: + r_code = f""" + library({data_package_name}) + genome_obj <- get(data_package_name) + len <- seqlengths(genome_obj)["{seqname}"] + if (is.na(len)) {{ + stop("Sequence '{seqname}' not found in package '{data_package_name}'.") + }} + cat("{seqname}:", len, "\\n") + """ + else: + r_code = f""" + library({data_package_name}) + genome_obj <- get(data_package_name) + lengths <- seqlengths(genome_obj) + for (i in seq_along(lengths)) {{ + cat(names(lengths)[i], ": ", lengths[i], "\\n", sep="") + }} + """ + return _run_r_command(r_code) \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-bsgenome/app/bioconductor-bsgenome_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-bsgenome/app/bioconductor-bsgenome_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..01e8596ffb589c227faf01eb2ff69ea3a1e2bd7e --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-bsgenome/app/bioconductor-bsgenome_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-bsgenome/app/bioconductor-bsgenome_server.py') +SERVER_NAME = 'biosci_bioconductor_bsgenome' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-cardspa/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-cardspa/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..53a99bf8cbf88e922ebdad2fbfe1ce6dd59da719 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-cardspa/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install bioconductor-cardspa via conda (e.g., from bioconda) +RUN conda install -c bioconda bioconductor-cardspa -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY app/bioconductor-cardspa_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/bioconductor-cardspa_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/bioconductor-cardspa_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-cardspa/app/bioconductor-cardspa_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-cardspa/app/bioconductor-cardspa_server.py new file mode 100644 index 0000000000000000000000000000000000000000..ec297386b892503c618230c6cdedfc3d4062884e --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-cardspa/app/bioconductor-cardspa_server.py @@ -0,0 +1,181 @@ +import subprocess +import tempfile +from pathlib import Path +from typing import Dict, List, Literal, Union + +# This is a placeholder for the MCP framework's decorator. +# The user's environment will provide the actual mcp.tool implementation. +class Mcp: + def tool(self): + def decorator(f): + return f + return decorator +mcp = Mcp() + + +@mcp.tool() +def cardspa( + sce_object: Path, + output_path: Path, + phenotype_col: str, + location_col_1: str = "row", + location_col_2: str = "col", + method: Literal["Giotto", "SpaGCN", "BayesSpace"] = "Giotto", + num_results: int = 10, + num_core: int = 1, + min_cells: int = 10, + min_pairs: int = 10, +) -> Dict[str, Union[str, List[str], int]]: + """ + Performs spatial analysis on single-cell data using the cardspa R package. + + This tool wraps the main `cardspa` function, which can utilize different + backend methods like Giotto, SpaGCN, or BayesSpace to identify significant + co-enrichment of cell types in spatial transcriptomics data. + + Args: + sce_object: Path to the input SingleCellExperiment object in RDS format. + output_path: Path to save the output results object in RDS format. + phenotype_col: The column name in the SCE object's colData that contains cell type annotations. + location_col_1: The column name for the first spatial coordinate (e.g., 'row', 'x'). + location_col_2: The column name for the second spatial coordinate (e.g., 'col', 'y'). + method: The computational method to use for the analysis. + num_results: The number of top co-enriched cell type pairs to return. + num_core: The number of CPU cores to use for parallel processing. + min_cells: The minimum number of cells required for a cell type to be included in the analysis. + min_pairs: The minimum number of interacting pairs for a result to be considered significant. + + Returns: + A dictionary containing the executed command, stdout, stderr, and a list of output files. + In case of an error, it includes an error message and return code. + """ + # --- Input Validation --- + if not sce_object.is_file(): + raise FileNotFoundError(f"Input file not found: {sce_object}") + + if not output_path.parent.is_dir(): + try: + output_path.parent.mkdir(parents=True, exist_ok=True) + except OSError as e: + raise NotADirectoryError( + f"Output directory does not exist and could not be created: {output_path.parent}" + ) from e + + if num_results <= 0: + raise ValueError("num_results must be a positive integer.") + if num_core <= 0: + raise ValueError("num_core must be a positive integer.") + if min_cells <= 0: + raise ValueError("min_cells must be a positive integer.") + if min_pairs <= 0: + raise ValueError("min_pairs must be a positive integer.") + + # R script to be executed. It includes package installation for portability. + r_script_content = """ + # run_cardspa.R + options(warn=-1) + suppressPackageStartupMessages({ + if (!requireNamespace("argparser", quietly = TRUE)) { + install.packages("argparser", repos = "http://cran.us.r-project.org", quiet = TRUE) + } + if (!requireNamespace("BiocManager", quietly = TRUE)) { + install.packages("BiocManager", repos = "http://cran.us.r-project.org", quiet = TRUE) + } + if (!requireNamespace("cardspa", quietly = TRUE)) { + BiocManager::install("cardspa", update=FALSE, ask=FALSE, quiet = TRUE) + } + if (!requireNamespace("SingleCellExperiment", quietly = TRUE)) { + BiocManager::install("SingleCellExperiment", update=FALSE, ask=FALSE, quiet = TRUE) + } + library(argparser) + library(cardspa) + library(SingleCellExperiment) + }) + + p <- arg_parser("Run cardspa analysis") + p <- add_argument(p, "--sce_object", help="Path to the input SingleCellExperiment RDS file", type="character") + p <- add_argument(p, "--output_path", help="Path to save the output RDS file", type="character") + p <- add_argument(p, "--phenotype_col", help="Column name for cell types", type="character") + p <- add_argument(p, "--location_col_1", help="First column name for spatial coordinates", default="row", type="character") + p <- add_argument(p, "--location_col_2", help="Second column name for spatial coordinates", default="col", type="character") + p <- add_argument(p, "--method", help="Method for spatial analysis (Giotto, SpaGCN, BayesSpace)", default="Giotto", type="character") + p <- add_argument(p, "--num_results", help="Number of top results to return", default=10, type="integer") + p <- add_argument(p, "--num_core", help="Number of cores for parallel processing", default=1, type="integer") + p <- add_argument(p, "--min_cells", help="Minimum number of cells for a cell type", default=10, type="integer") + p <- add_argument(p, "--min_pairs", help="Minimum number of interacting pairs", default=10, type="integer") + + argv <- parse_args(p) + + if (is.na(argv$sce_object) || is.na(argv$output_path) || is.na(argv$phenotype_col)) { + stop("Missing required arguments: --sce_object, --output_path, or --phenotype_col", call. = FALSE) + } + + cat("Reading input SCE object from:", argv$sce_object, "\\n") + sce <- readRDS(argv$sce_object) + + cat("Running cardspa analysis with method:", argv$method, "\\n") + results <- cardspa( + sce_object = sce, + location_col = c(argv$location_col_1, argv$location_col_2), + phenotype_col = argv$phenotype_col, + method = argv$method, + num_results = argv$num_results, + num_core = argv$num_core, + min_cells = argv$min_cells, + min_pairs = argv$min_pairs + ) + + cat("Saving results to:", argv$output_path, "\\n") + saveRDS(results, file = argv$output_path) + cat("cardspa analysis complete. Results saved to:", argv$output_path, "\\n") + """ + + # --- Subprocess Execution --- + with tempfile.NamedTemporaryFile(mode='w', suffix=".R", delete=False) as r_script_file: + r_script_file.write(r_script_content) + r_script_path = Path(r_script_file.name) + + cmd = [ + "Rscript", str(r_script_path), + "--sce_object", str(sce_object.resolve()), + "--output_path", str(output_path.resolve()), + "--phenotype_col", phenotype_col, + "--location_col_1", location_col_1, + "--location_col_2", location_col_2, + "--method", method, + "--num_results", str(num_results), + "--num_core", str(num_core), + "--min_cells", str(min_cells), + "--min_pairs", str(min_pairs), + ] + command_executed = " ".join(cmd) + + try: + result = subprocess.run( + cmd, + check=True, + capture_output=True, + text=True, + encoding='utf-8' + ) + return { + "command_executed": command_executed, + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": [str(output_path.resolve())] + } + except FileNotFoundError: + raise RuntimeError("Rscript command not found. Please ensure R is installed and in your system's PATH.") + except subprocess.CalledProcessError as e: + return { + "command_executed": command_executed, + "stdout": e.stdout, + "stderr": e.stderr, + "error": "cardspa execution failed.", + "return_code": e.returncode, + "output_files": [] + } + finally: + # Ensure temporary script is always removed + if r_script_path.exists(): + r_script_path.unlink() diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-cardspa/app/bioconductor-cardspa_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-cardspa/app/bioconductor-cardspa_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..a55562c398f4635d7bfe3f2efb1225952a6ac076 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-cardspa/app/bioconductor-cardspa_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-cardspa/app/bioconductor-cardspa_server.py') +SERVER_NAME = 'biosci_bioconductor_cardspa' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-cardspa/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-cardspa/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-cardspa/app/requirements.txt @@ -0,0 +1 @@ + diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-cardspa/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-cardspa/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..c97f759a1c64d8bf5d0c061ab68e38a779fc9afd --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-cardspa/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-bioconductor-cardspa: + build: . + image: mcp-bioconductor-cardspa:latest + container_name: mcp-bioconductor-cardspa + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=bioconductor-cardspa + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-cardspa/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-cardspa/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..7ea224b0da926f170bcd200087a22fdb529c04e4 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-cardspa/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - bioconductor-cardspa + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-cardspa/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-cardspa/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-cardspa/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-cellid/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-cellid/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..fe989065a06a14a78840560dd420a1b13cad37f1 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-cellid/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install bioconductor-cellid via conda (e.g., from bioconda) +RUN conda install -c bioconda bioconductor-cellid -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY app/bioconductor-cellid_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/bioconductor-cellid_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/bioconductor-cellid_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-cellid/app/bioconductor-cellid_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-cellid/app/bioconductor-cellid_server.py new file mode 100644 index 0000000000000000000000000000000000000000..120fd7c15988b989d30c90681a9c25200604244a --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-cellid/app/bioconductor-cellid_server.py @@ -0,0 +1,280 @@ +import subprocess +import os +from pathlib import Path +from typing import Optional, List, Union +import tempfile + +def run_r_command(script_content: str): + """Helper function to execute R code via Rscript""" + with tempfile.NamedTemporaryFile(mode='w', suffix='.R', delete=False) as tmp: + tmp.write(script_content) + tmp_path = tmp.name + + try: + result = subprocess.run( + ["Rscript", tmp_path], + capture_output=True, + text=True, + check=True + ) + return result.stdout, result.stderr + except subprocess.CalledProcessError as e: + raise RuntimeError(f"R execution failed: {e.stderr}\nStdout: {e.stdout}") + finally: + if os.path.exists(tmp_path): + os.remove(tmp_path) + +@mcp.tool() +def cellid_run_mca( + input_rds: str, + output_rds: str, + nmcs: int = 50, + assay: str = "RNA", + slot: str = "counts", + features: Optional[List[str]] = None +): + """ + Performs Multiple Correspondence Analysis (MCA) on a SingleCellExperiment or Seurat object. + MCA allows for the simultaneous representation of cells and genes in a common low-dimensional space. + """ + input_path = Path(input_rds) + output_path = Path(output_rds) + + if not input_path.exists(): + return {"error": f"Input file {input_rds} not found."} + + if nmcs <= 0: + return {"error": "nmcs must be a positive integer."} + + features_r = "NULL" + if features: + features_formatted = ", ".join([f"'{f}'" for f in features]) + features_r = f"c({features_formatted})" + + r_script = f""" + library(CelliD) + library(Seurat) + library(SingleCellExperiment) + + obj <- readRDS("{input_path}") + + # Run MCA + obj <- RunMCA(obj, nmcs = {nmcs}, assay = "{assay}", slot = "{slot}", features = {features_r}) + + saveRDS(obj, "{output_path}") + """ + + try: + stdout, stderr = run_r_command(r_script) + return { + "command_executed": "RunMCA", + "stdout": stdout, + "stderr": stderr, + "output_files": [str(output_path)] + } + except Exception as e: + return {"error": str(e)} + +@mcp.tool() +def cellid_get_cell_gene_set( + input_rds: str, + output_csv: str, + dims: int = 50, + n_features: int = 200 +): + """ + Extracts the top gene signatures for each individual cell based on MCA coordinates. + """ + input_path = Path(input_rds) + output_path = Path(output_csv) + + if not input_path.exists(): + return {"error": f"Input file {input_rds} not found."} + + if dims <= 0 or n_features <= 0: + return {"error": "dims and n_features must be positive integers."} + + r_script = f""" + library(CelliD) + library(Seurat) + library(SingleCellExperiment) + library(data.table) + + obj <- readRDS("{input_path}") + + # Extract gene sets per cell + # dims is treated as 1:dims + gene_sets <- GetCellGeneSet(obj, dims = 1:{dims}, n.features = {n_features}) + + # Convert list to a data table for export + # Each column is a cell, rows are top genes + max_len <- max(sapply(gene_sets, length)) + dt <- as.data.frame(lapply(gene_sets, function(x) c(x, rep(NA, max_len - length(x))))) + + write.csv(dt, "{output_path}", row.names = FALSE) + """ + + try: + stdout, stderr = run_r_command(r_script) + return { + "command_executed": "GetCellGeneSet", + "stdout": stdout, + "stderr": stderr, + "output_files": [str(output_path)] + } + except Exception as e: + return {"error": str(e)} + +@mcp.tool() +def cellid_run_cell_hgt( + input_rds: str, + pathways_gmt: str, + output_rds: str, + dims: int = 50, + n_features: int = 200, + min_size: int = 5 +): + """ + Performs cell-type assignment or functional enrichment using a Hypergeometric Test (HGT). + Matches per-cell gene signatures against a reference database (GMT format). + """ + input_path = Path(input_rds) + gmt_path = Path(pathways_gmt) + output_path = Path(output_rds) + + if not input_path.exists(): + return {"error": f"Input RDS {input_rds} not found."} + if not gmt_path.exists(): + return {"error": f"GMT file {pathways_gmt} not found."} + + r_script = f""" + library(CelliD) + library(Seurat) + library(SingleCellExperiment) + library(fgsea) + + obj <- readRDS("{input_path}") + pathways <- gmtPathways("{gmt_path}") + + # Run Hypergeometric Test + # Returns a matrix of -log10(p-value) + hgt_matrix <- RunCellHGT(obj, pathways = pathways, dims = 1:{dims}, n.features = {n_features}, minSize = {min_size}) + + # If Seurat, we can add it as an assay or metadata + if(inherits(obj, "Seurat")) {{ + obj[["HGT"]] <- CreateAssayObject(counts = hgt_matrix) + }} else {{ + # For SCE, add to altExp + altExp(obj, "HGT") <- SummarizedExperiment(assays = list(counts = hgt_matrix)) + }} + + saveRDS(obj, "{output_path}") + """ + + try: + stdout, stderr = run_r_command(r_script) + return { + "command_executed": "RunCellHGT", + "stdout": stdout, + "stderr": stderr, + "output_files": [str(output_path)] + } + except Exception as e: + return {"error": str(e)} + +@mcp.tool() +def cellid_run_gsea( + input_rds: str, + pathways_gmt: str, + output_rds: str, + dims: int = 50 +): + """ + Performs Gene Set Enrichment Analysis (GSEA) on cells using the MCA gene rankings. + """ + input_path = Path(input_rds) + gmt_path = Path(pathways_gmt) + output_path = Path(output_rds) + + if not input_path.exists(): + return {"error": f"Input RDS {input_rds} not found."} + if not gmt_path.exists(): + return {"error": f"GMT file {pathways_gmt} not found."} + + r_script = f""" + library(CelliD) + library(Seurat) + library(SingleCellExperiment) + library(fgsea) + + obj <- readRDS("{input_path}") + pathways <- gmtPathways("{gmt_path}") + + # Run GSEA + # This calculates enrichment scores for each cell and each pathway + gsea_res <- RunGSEA(obj, pathways = pathways, dims = 1:{dims}) + + # Save results (usually returns a matrix of NES) + if(inherits(obj, "Seurat")) {{ + obj[["GSEA"]] <- CreateAssayObject(counts = gsea_res) + }} else {{ + altExp(obj, "GSEA") <- SummarizedExperiment(assays = list(counts = gsea_res)) + }} + + saveRDS(obj, "{output_path}") + """ + + try: + stdout, stderr = run_r_command(r_script) + return { + "command_executed": "RunGSEA", + "stdout": stdout, + "stderr": stderr, + "output_files": [str(output_path)] + } + except Exception as e: + return {"error": str(e)} + +@mcp.tool() +def cellid_run_group_mca( + input_rds: str, + group_by: str, + output_rds: str, + nmcs: int = 50, + assay: str = "RNA", + slot: str = "counts" +): + """ + Performs MCA on grouped cells (e.g., by cluster or cell type) rather than individual cells. + Useful for extracting group-specific gene signatures. + """ + input_path = Path(input_rds) + output_path = Path(output_rds) + + if not input_path.exists(): + return {"error": f"Input file {input_rds} not found."} + + r_script = f""" + library(CelliD) + library(Seurat) + library(SingleCellExperiment) + + obj <- readRDS("{input_path}") + + # Run Group MCA + # This aggregates cells by the specified metadata column before MCA + group_mca <- RunGroupMCA(obj, group.by = "{group_by}", nmcs = {nmcs}, assay = "{assay}", slot = "{slot}") + + saveRDS(group_mca, "{output_path}") + """ + + try: + stdout, stderr = run_r_command(r_script) + return { + "command_executed": "RunGroupMCA", + "stdout": stdout, + "stderr": stderr, + "output_files": [str(output_path)] + } + except Exception as e: + return {"error": str(e)} \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-cellid/app/bioconductor-cellid_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-cellid/app/bioconductor-cellid_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..85aa4f4e18ee53ac062287a5f718e0ec276ea561 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-cellid/app/bioconductor-cellid_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-cellid/app/bioconductor-cellid_server.py') +SERVER_NAME = 'biosci_bioconductor_cellid' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-cellid/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-cellid/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..1314ea956fa156847970266461a3cfd9e0b3637f --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-cellid/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-bioconductor-cellid: + build: . + image: mcp-bioconductor-cellid:latest + container_name: mcp-bioconductor-cellid + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=bioconductor-cellid + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-cellid/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-cellid/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..f08c7d868e99c3a997a959a96e68214878543b56 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-cellid/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - bioconductor-cellid + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-cellid/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-cellid/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-cellid/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-cellmigration/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-cellmigration/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..993aab057245ddc0d90f7ad4bfda9cc195b99040 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-cellmigration/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install bioconductor-cellmigration via conda (e.g., from bioconda) +RUN conda install -c bioconda bioconductor-cellmigration -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY app/bioconductor-cellmigration_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/bioconductor-cellmigration_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/bioconductor-cellmigration_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-cellmigration/app/bioconductor-cellmigration_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-cellmigration/app/bioconductor-cellmigration_server.py new file mode 100644 index 0000000000000000000000000000000000000000..50a3013c0cf7a29a9f9e8af0e0e05aa6b0efc0b4 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-cellmigration/app/bioconductor-cellmigration_server.py @@ -0,0 +1,206 @@ +import subprocess +import tempfile +from pathlib import Path +from typing import List, Optional, Dict, Any + +# This is a placeholder for the MCP decorator. +# In a real MCP environment, this would be provided by the MCP framework. +class mcp: + @staticmethod + def tool(): + def decorator(func): + return func + return decorator + +@mcp.tool() +def run_cellmigration_analysis( + input_file: Path, + output_dir: Path, + delta_t: float = 1.0, + x_microns: float = 1.0, + y_microns: float = 1.0, + plot_format: str = "png", + plot_width: int = 7, + plot_height: int = 7, + plot_dpi: int = 300, + generate_displacement_plot: bool = True, + generate_speed_plot: bool = True, + generate_rose_plot: bool = True, + generate_angle_plot: bool = True, + generate_straighness_plot: bool = True, + generate_persistence_ratio_plot: bool = True, + generate_displacement_ratio_plot: bool = True, + generate_persistence_plot: bool = True, + generate_cumulative_plot: bool = True, + generate_cumulative_hist_plot: bool = True, + generate_single_cell_plot: bool = True, + generate_all_cells_plot: bool = True, +) -> Dict[str, Any]: + """ + Performs a complete cell migration analysis using the Bioconductor cellmigration package. + + This tool reads ImageJ trajectory data, performs a comprehensive analysis, + and generates a variety of plots and a summary statistics CSV file. + It requires R and the 'cellmigration' Bioconductor package to be installed. + + Args: + input_file: Path to the input data file from ImageJ's manual tracking plugin. + output_dir: Path to the directory where output files will be saved. + delta_t: Time interval between frames (dt). + x_microns: Pixel to micron conversion factor for the x-axis (x.scale). + y_microns: Pixel to micron conversion factor for the y-axis (y.scale). + plot_format: The format for the output plots ('png' or 'pdf'). + plot_width: Width of the plots in inches. + plot_height: Height of the plots in inches. + plot_dpi: Resolution for PNG plots in dots per inch. + generate_displacement_plot: Generate a displacement plot. + generate_speed_plot: Generate a speed plot. + generate_rose_plot: Generate a rose plot of cell turning angles. + generate_angle_plot: Generate a plot of cell turning angles. + generate_straighness_plot: Generate a plot of cell trajectory straightness. + generate_persistence_ratio_plot: Generate a persistence ratio plot. + generate_displacement_ratio_plot: Generate a displacement ratio plot. + generate_persistence_plot: Generate a persistence time plot. + generate_cumulative_plot: Generate a cumulative plot. + generate_cumulative_hist_plot: Generate a cumulative plot with a histogram. + generate_single_cell_plot: Generate a plot for each single cell trajectory. + generate_all_cells_plot: Generate a plot showing all cell trajectories. + + Returns: + A dictionary containing the executed command, stdout, stderr, and a list of output files. + """ + # --- Input Validation --- + if not input_file.is_file(): + raise FileNotFoundError(f"Input file not found: {input_file}") + + if delta_t <= 0: + raise ValueError("delta_t must be a positive number.") + if x_microns <= 0 or y_microns <= 0: + raise ValueError("x_microns and y_microns must be positive numbers.") + if plot_format not in ["png", "pdf"]: + raise ValueError("plot_format must be either 'png' or 'pdf'.") + if plot_width <= 0 or plot_height <= 0 or plot_dpi <= 0: + raise ValueError("Plot dimensions and DPI must be positive integers.") + + # --- File Path Handling --- + try: + output_dir.mkdir(parents=True, exist_ok=True) + except OSError as e: + raise OSError(f"Could not create output directory: {output_dir}. Error: {e}") + + # --- R Script Generation --- + # Using absolute paths to avoid issues with working directories + abs_input_file = input_file.resolve() + abs_output_dir = output_dir.resolve() + + # Helper function to generate R plotting code + def get_plot_code(plot_name: str, plot_function: str) -> str: + return f""" + cat("Generating {plot_name} plot...\\n") + if (plot_format == "png") {{ + png(file.path("{abs_output_dir}", "{plot_name}.png"), width=plot_width, height=plot_height, units="in", res=plot_dpi) + }} else {{ + pdf(file.path("{abs_output_dir}", "{plot_name}.pdf"), width=plot_width, height=plot_height) + }} + {plot_function}(analysis_results) + dev.off() + """ + + r_script_content = f""" + # Load the library, with error handling + if (!requireNamespace("cellmigration", quietly = TRUE)) {{ + stop("The 'cellmigration' package is not installed. Please install it from Bioconductor.", call. = FALSE) + }} + library(cellmigration) + + # --- Parameters --- + inputFile <- "{abs_input_file}" + outputDir <- "{abs_output_dir}" + dt <- {delta_t} + x_scale <- {x_microns} + y_scale <- {y_microns} + plot_format <- "{plot_format}" + plot_width <- {plot_width} + plot_height <- {plot_height} + plot_dpi <- {plot_dpi} + + # --- Main Analysis --- + cat("Reading ImageJ data from:", inputFile, "\\n") + ij_data <- read.ijdata(inputFile, dt=dt, x.scale=x_scale, y.scale=y_scale) + + cat("Analyzing trajectories...\\n") + analysis_results <- analyze.trajectories(ij_data) + + cat("Saving analysis summary...\\n") + write.csv(analysis_results$results, file=file.path(outputDir, "analysis_summary.csv"), row.names = FALSE) + + # --- Plotting --- + if ({str(generate_displacement_plot).upper()}) {{{get_plot_code('displacement', 'plot.displacement')}}} + if ({str(generate_speed_plot).upper()}) {{{get_plot_code('speed', 'plot.speed')}}} + if ({str(generate_rose_plot).upper()}) {{{get_plot_code('rose', 'plot.rose')}}} + if ({str(generate_angle_plot).upper()}) {{{get_plot_code('angle', 'plot.angle')}}} + if ({str(generate_straighness_plot).upper()}) {{{get_plot_code('straighness', 'plot.straighness')}}} + if ({str(generate_persistence_ratio_plot).upper()}) {{{get_plot_code('persistence_ratio', 'plot.persistence.ratio')}}} + if ({str(generate_displacement_ratio_plot).upper()}) {{{get_plot_code('displacement_ratio', 'plot.displacement.ratio')}}} + if ({str(generate_persistence_plot).upper()}) {{{get_plot_code('persistence', 'plot.persistence')}}} + if ({str(generate_cumulative_plot).upper()}) {{{get_plot_code('cumulative', 'plot.cummulative.plot')}}} + if ({str(generate_cumulative_hist_plot).upper()}) {{{get_plot_code('cumulative_hist', 'plot.cummulative.plot.and.hist')}}} + if ({str(generate_single_cell_plot).upper()}) {{{get_plot_code('single_cell', 'plot.single.cell')}}} + if ({str(generate_all_cells_plot).upper()}) {{{get_plot_code('all_cells', 'plot.all.cells')}}} + + cat("Analysis complete.\\n") + """ + + # --- Subprocess Execution --- + command = [] + stdout_str, stderr_str = "", "" + output_files = [] + + try: + with tempfile.NamedTemporaryFile(mode='w', suffix=".R", delete=False) as r_script_file: + r_script_file.write(r_script_content) + r_script_path = r_script_file.name + + command = ["Rscript", r_script_path] + process = subprocess.run( + command, + capture_output=True, + text=True, + check=True, + ) + stdout_str = process.stdout + stderr_str = process.stderr + + # Collect output files + output_files = [str(p) for p in abs_output_dir.glob('*')] + + except FileNotFoundError: + stderr_str = "Error: 'Rscript' command not found. Please ensure R is installed and in your system's PATH." + return { + "command_executed": " ".join(command), + "stdout": stdout_str, + "stderr": stderr_str, + "output_files": [] + } + except subprocess.CalledProcessError as e: + stderr_str = f"Error executing R script. Return code: {e.returncode}\n" + stderr_str += f"STDOUT:\n{e.stdout}\n" + stderr_str += f"STDERR:\n{e.stderr}\n" + return { + "command_executed": " ".join(command), + "stdout": e.stdout, + "stderr": stderr_str, + "output_files": [] + } + finally: + # Clean up the temporary R script + if 'r_script_path' in locals() and Path(r_script_path).exists(): + Path(r_script_path).unlink() + + # --- Structured Result Return --- + return { + "command_executed": " ".join(command), + "stdout": stdout_str, + "stderr": stderr_str, + "output_files": output_files + } \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-cellmigration/app/bioconductor-cellmigration_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-cellmigration/app/bioconductor-cellmigration_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..9b9eeb2dae5d8342a59d432bbfe6eadc197d1360 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-cellmigration/app/bioconductor-cellmigration_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-cellmigration/app/bioconductor-cellmigration_server.py') +SERVER_NAME = 'biosci_bioconductor_cellmigration' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-cellmigration/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-cellmigration/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-cellmigration/app/requirements.txt @@ -0,0 +1 @@ + diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-cellmigration/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-cellmigration/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-cellmigration/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-cotan/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-cotan/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..6b14e58239c11775ee3f73d0f681b3b3ddf905b8 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-cotan/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install bioconductor-cotan via conda (e.g., from bioconda) +RUN conda install -c bioconda bioconductor-cotan -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY app/bioconductor-cotan_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/bioconductor-cotan_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/bioconductor-cotan_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-cotan/app/bioconductor-cotan_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-cotan/app/bioconductor-cotan_server.py new file mode 100644 index 0000000000000000000000000000000000000000..fff61195e76d7223b6423248202059f92ff91d47 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-cotan/app/bioconductor-cotan_server.py @@ -0,0 +1,208 @@ +import subprocess +import tempfile +import textwrap +from pathlib import Path +from typing import List, Optional, Dict, Any + +# This is a placeholder for the MCP decorator. +# In a real MCP environment, this would be provided by the MCP framework. +class mcp: + @staticmethod + def tool(func): + def wrapper(*args, **kwargs): + return func(*args, **kwargs) + return wrapper + +@mcp.tool +def run_cotan_analysis( + raw_count_matrix: Path, + output_dir: Path, + min_reads_per_cell: int = 1000, + min_cells_per_gene: int = 3, + num_cores: int = 1, + run_gdi: bool = True, + run_clustering: bool = True, + num_clusters: int = 10, + save_cotan_object: bool = True, +) -> Dict[str, Any]: + """ + Performs a complete COTAN analysis for single-cell gene co-expression. + + This tool takes a raw UMI count matrix and runs the standard COTAN workflow, + which includes data cleaning, dispersion estimation, co-expression calculation, + Global Differentiation Index (GDI) analysis, and hierarchical clustering of genes. + + Args: + raw_count_matrix: Path to the input raw count matrix file (CSV format). + Rows should represent genes and columns represent cells. + The first column should contain gene names. + output_dir: Path to the directory where output files will be saved. + min_reads_per_cell: Minimum number of reads for a cell to be kept during cleaning. + min_cells_per_gene: Minimum number of cells where a gene must be expressed to be kept. + num_cores: Number of CPU cores to use for parallelizable steps. + run_gdi: If True, calculates the Global Differentiation Index (GDI) for all genes. + run_clustering: If True, performs hierarchical clustering on the gene co-expression matrix. + num_clusters: The number of clusters to create if `run_clustering` is True. + save_cotan_object: If True, saves the final COTAN R object as an RDS file for further analysis. + + Returns: + A dictionary containing the execution command, stdout, stderr, and a list of output file paths. + """ + # --- Input Validation --- + if not raw_count_matrix.is_file(): + raise FileNotFoundError(f"Input file not found: {raw_count_matrix}") + if min_reads_per_cell <= 0: + raise ValueError("min_reads_per_cell must be a positive integer.") + if min_cells_per_gene <= 0: + raise ValueError("min_cells_per_gene must be a positive integer.") + if num_cores <= 0: + raise ValueError("num_cores must be a positive integer.") + if run_clustering and num_clusters <= 1: + raise ValueError("num_clusters must be greater than 1 for clustering.") + + # --- File Path Handling --- + try: + output_dir.mkdir(parents=True, exist_ok=True) + except OSError as e: + raise OSError(f"Could not create output directory {output_dir}: {e}") + + output_files = [] + + # --- R Script Generation --- + # Note: R booleans are TRUE/FALSE + r_script_content = textwrap.dedent(f""" + # Load necessary libraries + library(COTAN) + library(Matrix) + + print("Starting COTAN analysis workflow...") + + # --- Parameters --- + raw_count_matrix_path <- "{raw_count_matrix.resolve()}" + output_dir_path <- "{output_dir.resolve()}" + min_reads <- {min_reads_per_cell} + min_cells <- {min_cells_per_gene} + num_cores <- {num_cores} + run_gdi_calc <- {"TRUE" if run_gdi else "FALSE"} + run_clust <- {"TRUE" if run_clustering else "FALSE"} + k_clusters <- {num_clusters} + save_obj <- {"TRUE" if save_cotan_object else "FALSE"} + + # --- Data Loading --- + print(paste("Loading raw count matrix from:", raw_count_matrix_path)) + # Using data.table for fast reading, assuming it's installed as a dependency + # or falling back to read.csv + if (requireNamespace("data.table", quietly = TRUE)) {{ + raw_data <- data.table::fread(raw_count_matrix_path, data.table = FALSE) + rownames(raw_data) <- raw_data[,1] + raw_data[,1] <- NULL + }} else {{ + raw_data <- read.csv(raw_count_matrix_path, header = TRUE, row.names = 1, check.names = FALSE) + }} + raw_data <- as.matrix(raw_data) + print("Data loaded successfully.") + + # --- COTAN Object Creation and Cleaning --- + obj <- COTAN(raw = raw_data) + print("COTAN object created.") + + obj <- clean(obj, min.reads = min_reads, min.cells = min_cells, calcExtraData = FALSE) + print("Data cleaning complete.") + + # --- Core Analysis --- + print("Estimating dispersion...") + obj <- estimateDispersion(obj, cores = num_cores) + + print("Calculating co-expression matrix...") + obj <- calculateCoex(obj, cores = num_cores) + + coex_df <- as.data.frame(as.matrix(getCoex(obj))) + coex_path <- file.path(output_dir_path, "coex_matrix.csv") + write.csv(coex_df, file = coex_path) + print(paste("Co-expression matrix saved to:", coex_path)) + + # --- GDI Calculation --- + if (run_gdi_calc) {{ + print("Calculating Global Differentiation Index (GDI)...") + obj <- calculateGDI(obj) + gdi_df <- getGDI(obj) + gdi_path <- file.path(output_dir_path, "gdi_table.csv") + write.csv(gdi_df, file = gdi_path) + print(paste("GDI table saved to:", gdi_path)) + }} + + # --- Clustering --- + if (run_clust) {{ + print("Performing hierarchical clustering on genes...") + coex_dist <- as.dist(1 - getCoex(obj)) + hc <- hclust(coex_dist, method = "ward.D2") + clusters <- cutree(hc, k = k_clusters) + clusters_df <- data.frame(gene = names(clusters), cluster = clusters) + clusters_path <- file.path(output_dir_path, "gene_clusters.csv") + write.csv(clusters_df, file = clusters_path, row.names = FALSE) + print(paste("Gene clusters saved to:", clusters_path)) + }} + + # --- Save Final Object --- + if (save_obj) {{ + rds_path <- file.path(output_dir_path, "cotan_object.rds") + saveRDS(obj, file = rds_path) + print(paste("COTAN object saved to:", rds_path)) + }} + + print("COTAN analysis completed successfully.") + """) + + # --- Subprocess Execution --- + with tempfile.TemporaryDirectory() as temp_dir: + script_path = Path(temp_dir) / "run_cotan.R" + with open(script_path, "w") as f: + f.write(r_script_content) + + command = ["Rscript", str(script_path)] + command_str = " ".join(command) + + try: + process = subprocess.run( + command, + capture_output=True, + text=True, + check=True, + ) + stdout = process.stdout + stderr = process.stderr + + # --- Collect Output Files --- + output_files.append(str(output_dir / "coex_matrix.csv")) + if run_gdi: + output_files.append(str(output_dir / "gdi_table.csv")) + if run_clustering: + output_files.append(str(output_dir / "gene_clusters.csv")) + if save_cotan_object: + output_files.append(str(output_dir / "cotan_object.rds")) + + # Verify that files were actually created + output_files = [p for p in output_files if Path(p).exists()] + + except FileNotFoundError: + return { + "command_executed": command_str, + "stdout": "", + "stderr": "Error: Rscript not found. Please ensure R is installed and in your PATH.", + "output_files": [], + } + except subprocess.CalledProcessError as e: + return { + "command_executed": command_str, + "stdout": e.stdout, + "stderr": e.stderr, + "output_files": [], + } + + # --- Structured Result Return --- + return { + "command_executed": command_str, + "stdout": stdout, + "stderr": stderr, + "output_files": output_files, + } \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-cotan/app/bioconductor-cotan_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-cotan/app/bioconductor-cotan_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..61397d80050f14705c67e20056692a6ae60d239c --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-cotan/app/bioconductor-cotan_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-cotan/app/bioconductor-cotan_server.py') +SERVER_NAME = 'biosci_bioconductor_cotan' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-cotan/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-cotan/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..e40f8dacf3543248285cbdb9e68bac1fd70bcf77 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-cotan/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-bioconductor-cotan: + build: . + image: mcp-bioconductor-cotan:latest + container_name: mcp-bioconductor-cotan + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=bioconductor-cotan + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-cotan/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-cotan/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..618896ac7d6d52c72c4937dca331e04fd907f73d --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-cotan/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - bioconductor-cotan + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-cotan/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-cotan/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-cotan/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-decipher/app/bioconductor-decipher_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-decipher/app/bioconductor-decipher_server.py new file mode 100644 index 0000000000000000000000000000000000000000..4d8adaa48f3ed68bcbb3f49dc0617d150ffdaacd --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-decipher/app/bioconductor-decipher_server.py @@ -0,0 +1,431 @@ +import subprocess +import tempfile +import textwrap +from pathlib import Path +from typing import Dict, Any, Optional, List + +# MCP decorators are used directly as the execution environment provides them. +# In a local development setup, you would use: +# from mcp import tool as mcp + +def _run_r_script( + r_script_content: str, + args: List[str], + output_files_map: Dict[str, Path] +) -> Dict[str, Any]: + """ + Helper function to execute an R script and return a structured result. + """ + cmd = ["Rscript", "-"] + args + # Create a user-friendly representation of the command + command_executed = f"echo '{r_script_content}' | Rscript - {' '.join(args)}" + + try: + result = subprocess.run( + cmd, + input=r_script_content, + capture_output=True, + text=True, + check=True, + ) + + # Verify that all expected output files were created and are not empty + for key, path in output_files_map.items(): + if not path.is_file() or path.stat().st_size == 0: + raise FileNotFoundError( + f"Expected output file '{path}' was not created or is empty. " + f"R script stderr: {result.stderr}" + ) + + return { + "command_executed": command_executed, + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": {k: str(v) for k, v in output_files_map.items()}, + } + except subprocess.CalledProcessError as e: + return { + "command_executed": command_executed, + "stdout": e.stdout, + "stderr": e.stderr, + "error": "R script execution failed.", + "return_code": e.returncode, + } + except FileNotFoundError as e: + return { + "command_executed": command_executed, + "stdout": "", + "stderr": str(e), + "error": "An output file was not generated as expected.", + } + + +@mcp.tool() +def align_sequences( + input_fasta: Path, + substitution_matrix: Optional[str] = None, + gap_opening: float = 10.0, + gap_extension: float = 0.1, + processors: int = 1, +) -> Dict[str, Any]: + """ + Performs multiple sequence alignment on sequences using DECIPHER's AlignSeqs. + + This tool aligns a set of DNA, RNA, or amino acid sequences provided in a FASTA file. + It automatically detects the sequence type. + + Args: + input_fasta: Path to the input FASTA file containing unaligned sequences. + substitution_matrix: The substitution matrix to use (e.g., 'BLOSUM62', 'PAM40'). + If None, DECIPHER will choose a default based on sequence type. + gap_opening: The penalty for opening a gap. + gap_extension: The penalty for extending a gap. + processors: The number of processors to use for alignment. + + Returns: + A dictionary containing the command executed, stdout, stderr, and a map of output files. + """ + if not input_fasta.is_file(): + raise FileNotFoundError(f"Input FASTA file not found: {input_fasta}") + if processors < 1: + raise ValueError("Number of processors must be at least 1.") + + r_script = textwrap.dedent(""" + # Load required libraries + suppressPackageStartupMessages(library(DECIPHER)) + + # Get command line arguments + args <- commandArgs(trailingOnly = TRUE) + inputFile <- args[1] + outputFile <- args[2] + subMatrix <- if (args[3] == "NULL") NULL else args[3] + gapOpen <- as.numeric(args[4]) + gapExt <- as.numeric(args[5]) + procs <- as.integer(args[6]) + + # Read sequences (readBStringSet is generic for DNA, RNA, AA) + seqs <- readBStringSet(inputFile) + + # Perform alignment + cat("Starting sequence alignment...\\n", file = stderr()) + aligned_seqs <- AlignSeqs(seqs, + gapOpening = gapOpen, + gapExtension = gapExt, + substitutionMatrix = subMatrix, + processors = procs, + verbose = TRUE) + cat("Alignment complete.\\n", file = stderr()) + + # Write the aligned sequences to the output file + writeXStringSet(aligned_seqs, filepath = outputFile) + cat("Output written to", outputFile, "\\n", file = stderr()) + """) + + with tempfile.TemporaryDirectory() as temp_dir: + temp_path = Path(temp_dir) + output_aligned_fasta = temp_path / "aligned.fasta" + + args = [ + str(input_fasta.resolve()), + str(output_aligned_fasta.resolve()), + str(substitution_matrix) if substitution_matrix else "NULL", + str(gap_opening), + str(gap_extension), + str(processors), + ] + + return _run_r_script(r_script, args, {"aligned_fasta": output_aligned_fasta}) + + +@mcp.tool() +def find_chimeras( + input_fasta: Path, + processors: int = 1, +) -> Dict[str, Any]: + """ + Identifies chimeric sequences in a FASTA file using DECIPHER's isChimera. + + This tool screens a set of sequences (typically 16S rRNA amplicons) and + identifies which ones are likely to be chimeras formed during PCR. + + Args: + input_fasta: Path to the input FASTA file containing sequences to check. + processors: The number of processors to use for chimera detection. + + Returns: + A dictionary containing the command executed, stdout, stderr, and a map of output files. + """ + if not input_fasta.is_file(): + raise FileNotFoundError(f"Input FASTA file not found: {input_fasta}") + if processors < 1: + raise ValueError("Number of processors must be at least 1.") + + r_script = textwrap.dedent(""" + suppressPackageStartupMessages(library(DECIPHER)) + + args <- commandArgs(trailingOnly = TRUE) + inputFile <- args[1] + outputFile <- args[2] + procs <- as.integer(args[3]) + + cat("Reading sequences...\\n", file = stderr()) + seqs <- readDNAStringSet(inputFile) + + cat("Finding chimeras...\\n", file = stderr()) + chimera_flags <- isChimera(seqs, processors = procs, verbose = TRUE) + + chimera_names <- names(seqs)[chimera_flags] + + cat(length(chimera_names), "chimeras found.\\n", file = stderr()) + writeLines(chimera_names, con = outputFile) + cat("Chimeric sequence names written to", outputFile, "\\n", file = stderr()) + """) + + with tempfile.TemporaryDirectory() as temp_dir: + temp_path = Path(temp_dir) + output_chimera_list = temp_path / "chimeric_sequences.txt" + + args = [ + str(input_fasta.resolve()), + str(output_chimera_list.resolve()), + str(processors), + ] + + return _run_r_script(r_script, args, {"chimera_list": output_chimera_list}) + + +@mcp.tool() +def classify_sequences( + query_fasta: Path, + training_set: Path, + confidence_threshold: float = 0.7, + processors: int = 1, +) -> Dict[str, Any]: + """ + Classifies sequences using a pre-trained DECIPHER model (IdTaxa). + + This tool assigns taxonomic classification to sequences based on a provided + training set file (e.g., SILVA, RDP, Greengenes). + + Args: + query_fasta: Path to the FASTA file of sequences to classify. + training_set: Path to the .RData file containing the training set object. + confidence_threshold: The minimum confidence required to assign a taxonomy. + processors: The number of processors to use for classification. + + Returns: + A dictionary containing the command executed, stdout, stderr, and a map of output files. + """ + if not query_fasta.is_file(): + raise FileNotFoundError(f"Query FASTA file not found: {query_fasta}") + if not training_set.is_file(): + raise FileNotFoundError(f"Training set file not found: {training_set}") + if not 0.0 <= confidence_threshold <= 1.0: + raise ValueError("Confidence threshold must be between 0.0 and 1.0.") + if processors < 1: + raise ValueError("Number of processors must be at least 1.") + + r_script = textwrap.dedent(""" + suppressPackageStartupMessages(library(DECIPHER)) + + args <- commandArgs(trailingOnly = TRUE) + queryFile <- args[1] + trainingFile <- args[2] + outputFile <- args[3] + conf <- as.numeric(args[4]) + procs <- as.integer(args[5]) + + cat("Reading query sequences...\\n", file = stderr()) + seqs <- readDNAStringSet(queryFile) + + cat("Loading training set...\\n", file = stderr()) + load(trainingFile) # Assumes this loads an object named 'trainingSet' + + cat("Classifying sequences with IdTaxa...\\n", file = stderr()) + ids <- IdTaxa(seqs, trainingSet, threshold = conf, processors = procs, verbose = TRUE) + + # Format the output into a table + get_taxonomy <- function(x) { + if (length(x) > 0 && length(x[[1]]$taxon) > 0) { + paste(x[[1]]$taxon, collapse=";") + } else { + "Unclassified" + } + } + taxonomy_strings <- sapply(ids, get_taxonomy) + output_df <- data.frame(sequence_name = names(seqs), taxonomy = taxonomy_strings) + + cat("Writing classification results...\\n", file = stderr()) + write.table(output_df, file = outputFile, sep = "\\t", row.names = FALSE, quote = FALSE) + cat("Output written to", outputFile, "\\n", file = stderr()) + """) + + with tempfile.TemporaryDirectory() as temp_dir: + temp_path = Path(temp_dir) + output_classification_tsv = temp_path / "classification.tsv" + + args = [ + str(query_fasta.resolve()), + str(training_set.resolve()), + str(output_classification_tsv.resolve()), + str(confidence_threshold), + str(processors), + ] + + return _run_r_script(r_script, args, {"classification_results": output_classification_tsv}) + + +@mcp.tool() +def design_primers( + input_aligned_fasta: Path, + min_length: int = 18, + max_length: int = 25, + min_tm: float = 50.0, + max_tm: float = 65.0, + min_gc: float = 40.0, + max_gc: float = 60.0, + max_degeneracy: int = 4, + processors: int = 1, +) -> Dict[str, Any]: + """ + Designs group-specific PCR primers from an aligned FASTA file using DesignPrimers. + + Args: + input_aligned_fasta: Path to an aligned FASTA file of target sequences. + min_length: Minimum primer length. + max_length: Maximum primer length. + min_tm: Minimum melting temperature (°C). + max_tm: Maximum melting temperature (°C). + min_gc: Minimum GC content (percent). + max_gc: Maximum GC content (percent). + max_degeneracy: Maximum number of degenerate bases allowed in a primer. + processors: The number of processors to use. + + Returns: + A dictionary containing the command executed, stdout, stderr, and a map of output files. + """ + if not input_aligned_fasta.is_file(): + raise FileNotFoundError(f"Input aligned FASTA file not found: {input_aligned_fasta}") + if processors < 1: + raise ValueError("Number of processors must be at least 1.") + + r_script = textwrap.dedent(""" + suppressPackageStartupMessages(library(DECIPHER)) + + args <- commandArgs(trailingOnly = TRUE) + inputFile <- args[1] + outputFile <- args[2] + minL <- as.integer(args[3]) + maxL <- as.integer(args[4]) + minTm <- as.numeric(args[5]) + maxTm <- as.numeric(args[6]) + minGC <- as.numeric(args[7]) + maxGC <- as.numeric(args[8]) + maxD <- as.integer(args[9]) + procs <- as.integer(args[10]) + + cat("Reading aligned sequences...\\n", file = stderr()) + seqs <- readDNAStringSet(inputFile) + + cat("Designing primers...\\n", file = stderr()) + primers <- DesignPrimers( + seqs, minLength = minL, maxLength = maxL, minTm = minTm, maxTm = maxTm, + minGC = minGC, maxGC = maxGC, maxDegeneracy = maxD, + processors = procs, verbose = TRUE + ) + + cat(nrow(primers), "candidate primers found.\\n", file = stderr()) + write.table(primers, file = outputFile, sep = "\\t", row.names = FALSE, quote = FALSE) + cat("Primer table written to", outputFile, "\\n", file = stderr()) + """) + + with tempfile.TemporaryDirectory() as temp_dir: + temp_path = Path(temp_dir) + output_primers_tsv = temp_path / "primers.tsv" + + args = [ + str(input_aligned_fasta.resolve()), + str(output_primers_tsv.resolve()), + str(min_length), str(max_length), str(min_tm), str(max_tm), + str(min_gc), str(max_gc), str(max_degeneracy), str(processors), + ] + + return _run_r_script(r_script, args, {"designed_primers": output_primers_tsv}) + + +@mcp.tool() +def design_probes( + input_aligned_fasta: Path, + min_length: int = 18, + max_length: int = 50, + min_tm: float = 60.0, + max_tm: float = 80.0, + min_gc: float = 40.0, + max_gc: float = 60.0, + max_degeneracy: int = 1, + processors: int = 1, +) -> Dict[str, Any]: + """ + Designs hybridization probes (e.g., for FISH) from an aligned FASTA file using DesignProbes. + + Args: + input_aligned_fasta: Path to an aligned FASTA file of target sequences. + min_length: Minimum probe length. + max_length: Maximum probe length. + min_tm: Minimum melting temperature (°C). + max_tm: Maximum melting temperature (°C). + min_gc: Minimum GC content (percent). + max_gc: Maximum GC content (percent). + max_degeneracy: Maximum number of degenerate bases allowed in a probe. + processors: The number of processors to use. + + Returns: + A dictionary containing the command executed, stdout, stderr, and a map of output files. + """ + if not input_aligned_fasta.is_file(): + raise FileNotFoundError(f"Input aligned FASTA file not found: {input_aligned_fasta}") + if processors < 1: + raise ValueError("Number of processors must be at least 1.") + + r_script = textwrap.dedent(""" + suppressPackageStartupMessages(library(DECIPHER)) + + args <- commandArgs(trailingOnly = TRUE) + inputFile <- args[1] + outputFile <- args[2] + minL <- as.integer(args[3]) + maxL <- as.integer(args[4]) + minTm <- as.numeric(args[5]) + maxTm <- as.numeric(args[6]) + minGC <- as.numeric(args[7]) + maxGC <- as.numeric(args[8]) + maxD <- as.integer(args[9]) + procs <- as.integer(args[10]) + + cat("Reading aligned sequences...\\n", file = stderr()) + seqs <- readDNAStringSet(inputFile) + + cat("Designing probes...\\n", file = stderr()) + probes <- DesignProbes( + seqs, minLength = minL, maxLength = maxL, minTm = minTm, maxTm = maxTm, + minGC = minGC, maxGC = maxGC, maxDegeneracy = maxD, + processors = procs, verbose = TRUE + ) + + cat(nrow(probes), "candidate probes found.\\n", file = stderr()) + write.table(probes, file = outputFile, sep = "\\t", row.names = FALSE, quote = FALSE) + cat("Probe table written to", outputFile, "\\n", file = stderr()) + """) + + with tempfile.TemporaryDirectory() as temp_dir: + temp_path = Path(temp_dir) + output_probes_tsv = temp_path / "probes.tsv" + + args = [ + str(input_aligned_fasta.resolve()), + str(output_probes_tsv.resolve()), + str(min_length), str(max_length), str(min_tm), str(max_tm), + str(min_gc), str(max_gc), str(max_degeneracy), str(processors), + ] + + return _run_r_script(r_script, args, {"designed_probes": output_probes_tsv}) \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-decipher/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-decipher/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-decipher/app/requirements.txt @@ -0,0 +1 @@ + diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-decontx/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-decontx/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..820ab9780370a368b6c65cf4a3dfb8651f02dc92 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-decontx/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install bioconductor-decontx via conda (e.g., from bioconda) +RUN conda install -c bioconda bioconductor-decontx -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY app/bioconductor-decontx_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/bioconductor-decontx_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/bioconductor-decontx_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-decontx/app/bioconductor-decontx_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-decontx/app/bioconductor-decontx_server.py new file mode 100644 index 0000000000000000000000000000000000000000..1f7910b0003b3bc6928b53699877744209ebf73d --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-decontx/app/bioconductor-decontx_server.py @@ -0,0 +1,326 @@ +import subprocess +import tempfile +from pathlib import Path +from typing import Optional + +# @mcp.tool() +def decontx( + x: Path, + output_prefix: str, + z: Optional[Path] = None, + batch: Optional[Path] = None, + max_iter: int = 500, + delta_a: float = 1.0, + delta_b: float = 10.0, + phi: Optional[Path] = None, + theta: Optional[Path] = None, + effective_length: Optional[Path] = None, + length_normalization: str = "tpm", + num_chains: int = 3, + num_cores: int = 1, + seed: int = 12345, + logfile: Optional[Path] = None, + verbose: bool = True, +): + """ + Estimate and remove RNA contamination in single-cell data using DecontX. + + This tool wraps the decontX R function to identify and remove contamination + from ambient RNA in single-cell RNA-seq count data. + + Args: + x: Path to the input count matrix file (e.g., CSV). Rows should be + genes and columns should be cells. The first column should contain + gene names. + output_prefix: Prefix for the output files. The directory will be + created if it doesn't exist. + z: Optional path to a file containing cell labels. One label per line. + batch: Optional path to a file containing batch labels. One label per line. + max_iter: Maximum number of iterations for the Gibbs sampler. + delta_a: The first concentration parameter for the Dirichlet prior on + contamination levels. + delta_b: The second concentration parameter for the Dirichlet prior on + contamination levels. + phi: Optional path to a file containing the background contamination + distribution. If not provided, it will be estimated. + theta: Optional path to a file containing the native expression + distribution for each cell. If not provided, it will be estimated. + effective_length: Optional path to a file containing effective transcript + lengths for length normalization. + length_normalization: The length normalization method to be used. + One of 'tpm', 'fpm', 'fpkm', 'none'. + num_chains: The number of MCMC chains to run. + num_cores: The number of cores to use for parallel processing. + seed: The seed for the random number generator. + logfile: Optional path to a file where logs will be written. + verbose: If True, print progress messages to stderr. + """ + # --- Input Validation --- + if not x.is_file(): + raise FileNotFoundError(f"Input count matrix file not found: {x}") + if z and not z.is_file(): + raise FileNotFoundError(f"Cell labels file not found: {z}") + if batch and not batch.is_file(): + raise FileNotFoundError(f"Batch labels file not found: {batch}") + if phi and not phi.is_file(): + raise FileNotFoundError(f"Phi distribution file not found: {phi}") + if theta and not theta.is_file(): + raise FileNotFoundError(f"Theta distribution file not found: {theta}") + if effective_length and not effective_length.is_file(): + raise FileNotFoundError(f"Effective length file not found: {effective_length}") + + if max_iter <= 0: + raise ValueError("max_iter must be a positive integer.") + if num_chains <= 0: + raise ValueError("num_chains must be a positive integer.") + if num_cores <= 0: + raise ValueError("num_cores must be a positive integer.") + if delta_a <= 0 or delta_b <= 0: + raise ValueError("Delta parameters (delta_a, delta_b) must be positive.") + + allowed_norm = ["tpm", "fpm", "fpkm", "none"] + if length_normalization not in allowed_norm: + raise ValueError(f"length_normalization must be one of {allowed_norm}") + + # --- File and Path Handling --- + output_dir = Path(output_prefix).parent + output_dir.mkdir(parents=True, exist_ok=True) + + output_counts_path = Path(f"{output_prefix}_decontXcounts.csv") + output_contamination_path = Path(f"{output_prefix}_contamination.csv") + + with tempfile.TemporaryDirectory() as temp_dir: + script_path = Path(temp_dir) / "run_decontx.R" + + # --- R Script Generation --- + z_r = f'"{z.resolve()}"' if z else "NULL" + batch_r = f'"{batch.resolve()}"' if batch else "NULL" + phi_r = f'read.csv("{phi.resolve()}", header=FALSE)$V1' if phi else "NULL" + theta_r = f'read.csv("{theta.resolve()}", header=FALSE)$V1' if theta else "NULL" + effective_length_r = f'read.csv("{effective_length.resolve()}", header=FALSE)$V1' if effective_length else "NULL" + logfile_r = f'"{logfile.resolve()}"' if logfile else "NULL" + verbose_r = "TRUE" if verbose else "FALSE" + + r_script = f""" + library(decontX) + library(SingleCellExperiment) + library(Matrix) + + counts <- as.matrix(read.csv("{x.resolve()}", row.names=1, check.names=FALSE)) + + z_data <- if (!is.null({z_r})) read.csv({z_r}, header=FALSE, stringsAsFactors=TRUE)$V1 else NULL + batch_data <- if (!is.null({batch_r})) read.csv({batch_r}, header=FALSE, stringsAsFactors=TRUE)$V1 else NULL + phi_data <- if (!is.null({phi_r})) {phi_r} else NULL + theta_data <- if (!is.null({theta_r})) {theta_r} else NULL + effectiveLength_data <- if (!is.null({effective_length_r})) {effective_length_r} else NULL + + sce <- SingleCellExperiment(assays = list(counts = counts)) + + set.seed({seed}) + sce <- decontX( + x = sce, + z = z_data, + batch = batch_data, + maxIter = {max_iter}, + delta = c({delta_a}, {delta_b}), + phi = phi_data, + theta = theta_data, + effectiveLength = effectiveLength_data, + lengthNormalization = "{length_normalization}", + numChains = {num_chains}, + numCores = {num_cores}, + logfile = {logfile_r}, + verbose = {verbose_r} + ) + + decontaminated_counts <- decontXcounts(sce) + contamination_scores <- colData(sce)$decontX_contamination + results_df <- data.frame(contamination = contamination_scores, row.names = colnames(sce)) + + write.csv(as.matrix(decontaminated_counts), file="{output_counts_path.resolve()}", row.names=TRUE) + write.csv(results_df, file="{output_contamination_path.resolve()}", row.names=TRUE) + """ + + with open(script_path, "w") as f: + f.write(r_script) + + # --- Subprocess Execution --- + command = ["Rscript", str(script_path)] + command_str = " ".join(map(str, command)) + + try: + result = subprocess.run( + command, + capture_output=True, + text=True, + check=True, + ) + + output_files = [ + str(output_counts_path), + str(output_contamination_path) + ] + + return { + "command_executed": command_str, + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": output_files + } + except subprocess.CalledProcessError as e: + return { + "command_executed": command_str, + "stdout": e.stdout, + "stderr": e.stderr, + "error": "DecontX execution failed.", + "return_code": e.returncode + } + +# @mcp.tool() +def decontpro( + x: Path, + output_prefix: str, + z: Optional[Path] = None, + batch: Optional[Path] = None, + max_iter: int = 200, + delta_a: float = 1.0, + delta_b: float = 1.0, + gamma: float = 1.0, + beta: float = 1.0, + num_chains: int = 3, + num_cores: int = 1, + seed: int = 12345, + logfile: Optional[Path] = None, + verbose: bool = True, +): + """ + Estimate and remove protein contamination in CITE-seq data using DecontPro. + + This tool wraps the decontPro R function to identify and remove contamination + from ambient and background sources in single-cell protein expression data. + + Args: + x: Path to the input ADT count matrix file (e.g., CSV). Rows should be + proteins and columns should be cells. The first column should contain + protein names. + output_prefix: Prefix for the output files. The directory will be + created if it doesn't exist. + z: Optional path to a file containing cell labels. One label per line. + batch: Optional path to a file containing batch labels. One label per line. + max_iter: Maximum number of iterations for the Gibbs sampler. + delta_a: The first concentration parameter for the Dirichlet prior on + contamination levels. + delta_b: The second concentration parameter for the Dirichlet prior on + contamination levels. + gamma: The background contamination distribution parameter. + beta: The native expression distribution parameter for each cell. + num_chains: The number of MCMC chains to run. + num_cores: The number of cores to use for parallel processing. + seed: The seed for the random number generator. + logfile: Optional path to a file where logs will be written. + verbose: If True, print progress messages to stderr. + """ + # --- Input Validation --- + if not x.is_file(): + raise FileNotFoundError(f"Input ADT count matrix file not found: {x}") + if z and not z.is_file(): + raise FileNotFoundError(f"Cell labels file not found: {z}") + if batch and not batch.is_file(): + raise FileNotFoundError(f"Batch labels file not found: {batch}") + + if max_iter <= 0: + raise ValueError("max_iter must be a positive integer.") + if num_chains <= 0: + raise ValueError("num_chains must be a positive integer.") + if num_cores <= 0: + raise ValueError("num_cores must be a positive integer.") + if delta_a <= 0 or delta_b <= 0: + raise ValueError("Delta parameters (delta_a, delta_b) must be positive.") + if gamma <= 0 or beta <= 0: + raise ValueError("Gamma and Beta parameters must be positive.") + + # --- File and Path Handling --- + output_dir = Path(output_prefix).parent + output_dir.mkdir(parents=True, exist_ok=True) + + output_counts_path = Path(f"{output_prefix}_decontPro_counts.csv") + output_contamination_path = Path(f"{output_prefix}_decontPro_contamination.csv") + + with tempfile.TemporaryDirectory() as temp_dir: + script_path = Path(temp_dir) / "run_decontpro.R" + + # --- R Script Generation --- + z_r = f'"{z.resolve()}"' if z else "NULL" + batch_r = f'"{batch.resolve()}"' if batch else "NULL" + logfile_r = f'"{logfile.resolve()}"' if logfile else "NULL" + verbose_r = "TRUE" if verbose else "FALSE" + + r_script = f""" + library(decontX) + library(SingleCellExperiment) + library(Matrix) + + counts <- as.matrix(read.csv("{x.resolve()}", row.names=1, check.names=FALSE)) + + z_data <- if (!is.null({z_r})) read.csv({z_r}, header=FALSE, stringsAsFactors=TRUE)$V1 else NULL + batch_data <- if (!is.null({batch_r})) read.csv({batch_r}, header=FALSE, stringsAsFactors=TRUE)$V1 else NULL + + sce <- SingleCellExperiment(assays = list(counts = counts)) + + set.seed({seed}) + sce <- decontPro( + x = sce, + z = z_data, + batch = batch_data, + maxIter = {max_iter}, + delta = c({delta_a}, {delta_b}), + gamma = {gamma}, + beta = {beta}, + numChains = {num_chains}, + numCores = {num_cores}, + logfile = {logfile_r}, + verbose = {verbose_r} + ) + + decontaminated_counts <- metadata(sce)$decontPro$decontPro_counts + contamination_scores <- colData(sce)$decontPro_contamination + results_df <- data.frame(contamination = contamination_scores, row.names = colnames(sce)) + + write.csv(as.matrix(decontaminated_counts), file="{output_counts_path.resolve()}", row.names=TRUE) + write.csv(results_df, file="{output_contamination_path.resolve()}", row.names=TRUE) + """ + + with open(script_path, "w") as f: + f.write(r_script) + + # --- Subprocess Execution --- + command = ["Rscript", str(script_path)] + command_str = " ".join(map(str, command)) + + try: + result = subprocess.run( + command, + capture_output=True, + text=True, + check=True, + ) + + output_files = [ + str(output_counts_path), + str(output_contamination_path) + ] + + return { + "command_executed": command_str, + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": output_files + } + except subprocess.CalledProcessError as e: + return { + "command_executed": command_str, + "stdout": e.stdout, + "stderr": e.stderr, + "error": "DecontPro execution failed.", + "return_code": e.returncode + } \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-decontx/app/bioconductor-decontx_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-decontx/app/bioconductor-decontx_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..8ac7d72c760f9ca4c2e538853c842051ee91acce --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-decontx/app/bioconductor-decontx_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-decontx/app/bioconductor-decontx_server.py') +SERVER_NAME = 'biosci_bioconductor_decontx' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-decontx/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-decontx/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..54278d77b94074aee3436b6a210fcfbe16409ac2 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-decontx/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-bioconductor-decontx: + build: . + image: mcp-bioconductor-decontx:latest + container_name: mcp-bioconductor-decontx + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=bioconductor-decontx + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-decontx/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-decontx/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..494d5271b04b4f4c9ab6c0e7d89038eb55a793e2 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-decontx/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - bioconductor-decontx + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-decontx/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-decontx/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-decontx/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-deseq2/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-deseq2/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..6ce2c11f459604c91a335ffbaba16221d791a713 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-deseq2/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install bioconductor-deseq2 via conda (e.g., from bioconda) +RUN conda install -c bioconda bioconductor-deseq2 -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY app/bioconductor-deseq2_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/bioconductor-deseq2_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/bioconductor-deseq2_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-deseq2/app/bioconductor-deseq2_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-deseq2/app/bioconductor-deseq2_server.py new file mode 100644 index 0000000000000000000000000000000000000000..e415a1165d8d953e2f5af3c66effaf76f3ef8c07 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-deseq2/app/bioconductor-deseq2_server.py @@ -0,0 +1,291 @@ +import subprocess +import tempfile +from pathlib import Path +from typing import Optional, List, Dict, Any + +@mcp.tool() +def deseq2_differential_expression( + counts_path: str, + metadata_path: str, + design_formula: str, + output_results_path: str, + contrast_column: Optional[str] = None, + reference_level: Optional[str] = None, + treated_level: Optional[str] = None, + alpha: float = 0.05, + lfc_threshold: float = 0.0, + test_type: str = "Wald", + fit_type: str = "parametric", + reduced_formula: Optional[str] = None, + shrink_lfc: bool = True, + shrink_type: str = "apeglm" +): + """ + Performs differential expression analysis using DESeq2. + + Args: + counts_path: Path to the raw counts matrix (CSV/TSV, genes as rows, samples as columns). + metadata_path: Path to the sample metadata (CSV/TSV, samples as rows). + design_formula: The design formula (e.g., '~ condition' or '~ genotype + condition'). + output_results_path: Path where the results CSV will be saved. + contrast_column: The column in metadata to use for comparison. + reference_level: The baseline/control level in the contrast column. + treated_level: The treatment level in the contrast column. + alpha: Significance threshold (FDR). + lfc_threshold: Log2 fold change threshold for null hypothesis. + test_type: 'Wald' or 'LRT' (Likelihood Ratio Test). + fit_type: 'parametric', 'local', or 'mean'. + reduced_formula: Required if test_type is 'LRT' (e.g., '~ 1'). + shrink_lfc: Whether to apply Log2 Fold Change shrinkage. + shrink_type: Type of shrinkage estimator ('apeglm', 'ashr', or 'normal'). + """ + counts_p = Path(counts_path) + meta_p = Path(metadata_path) + out_p = Path(output_results_path) + + if not counts_p.exists(): + raise FileNotFoundError(f"Counts file not found: {counts_path}") + if not meta_p.exists(): + raise FileNotFoundError(f"Metadata file not found: {metadata_path}") + if test_type == "LRT" and not reduced_formula: + raise ValueError("reduced_formula is required when test_type is 'LRT'") + + # Construct R script + r_script = f""" + library(DESeq2) + + # Load data + counts <- read.csv("{counts_p}", row.names=1, check.names=FALSE) + coldata <- read.csv("{meta_p}", row.names=1, check.names=FALSE) + + # Ensure factors + for(col in colnames(coldata)) {{ + if(is.character(coldata[[col]])) coldata[[col]] <- as.factor(coldata[[col]]) + }} + + # Create DESeqDataSet + dds <- DESeqDataSetFromMatrix(countData=counts, colData=coldata, design=as.formula("{design_formula}")) + + # Run DESeq + if ("{test_type}" == "LRT") {{ + dds <- DESeq(dds, test="LRT", reduced=as.formula("{reduced_formula}"), fitType="{fit_type}") + }} else {{ + dds <- DESeq(dds, test="Wald", fitType="{fit_type}") + }} + + # Results extraction + if (!is.null("{contrast_column}") && !is.null("{reference_level}") && !is.null("{treated_level}")) {{ + res <- results(dds, contrast=c("{contrast_column}", "{treated_level}", "{reference_level}"), + alpha={alpha}, lfcThreshold={lfc_threshold}) + + if ({str(shrink_lfc).upper()}) {{ + # Shrinkage requires the coefficient name or contrast + # apeglm is preferred but requires 'coef' + res <- lfcShrink(dds, contrast=c("{contrast_column}", "{treated_level}", "{reference_level}"), + res=res, type="{shrink_type}") + }} + }} else {{ + res <- results(dds, alpha={alpha}, lfcThreshold={lfc_threshold}) + }} + + write.csv(as.data.frame(res), file="{out_p}") + """ + + with tempfile.NamedTemporaryFile(suffix=".R", mode="w", delete=False) as tmp: + tmp.write(r_script) + tmp_path = tmp.name + + try: + cmd = ["Rscript", tmp_path] + result = subprocess.run(cmd, capture_output=True, text=True, check=True) + return { + "command_executed": " ".join(cmd), + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": [str(out_p)] + } + except subprocess.CalledProcessError as e: + return { + "error": "DESeq2 execution failed", + "stdout": e.stdout, + "stderr": e.stderr, + "command_executed": " ".join(e.cmd) + } + finally: + Path(tmp_path).unlink(missing_ok=True) + +@mcp.tool() +def deseq2_transform_counts( + counts_path: str, + metadata_path: str, + design_formula: str, + output_path: str, + method: str = "vst", + blind: bool = True +): + """ + Transforms count data using Variance Stabilizing Transformation (VST) or Regularized Log (rlog). + Useful for PCA, clustering, and visualization. + + Args: + counts_path: Path to raw counts matrix. + metadata_path: Path to sample metadata. + design_formula: Design formula (e.g., '~ condition'). + output_path: Path to save the transformed matrix CSV. + method: Transformation method ('vst' or 'rlog'). + blind: Whether to perform transformation in a blind manner (independent of design). + """ + counts_p = Path(counts_path) + meta_p = Path(metadata_path) + out_p = Path(output_path) + + if method not in ["vst", "rlog"]: + raise ValueError("Method must be 'vst' or 'rlog'") + + r_script = f""" + library(DESeq2) + counts <- read.csv("{counts_p}", row.names=1, check.names=FALSE) + coldata <- read.csv("{meta_p}", row.names=1, check.names=FALSE) + dds <- DESeqDataSetFromMatrix(countData=counts, colData=coldata, design=as.formula("{design_formula}")) + + if ("{method}" == "vst") {{ + transformed <- vst(dds, blind={str(blind).upper()}) + }} else {{ + transformed <- rlog(dds, blind={str(blind).upper()}) + }} + + write.csv(assay(transformed), file="{out_p}") + """ + + with tempfile.NamedTemporaryFile(suffix=".R", mode="w", delete=False) as tmp: + tmp.write(r_script) + tmp_path = tmp.name + + try: + cmd = ["Rscript", tmp_path] + result = subprocess.run(cmd, capture_output=True, text=True, check=True) + return { + "command_executed": " ".join(cmd), + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": [str(out_p)] + } + except subprocess.CalledProcessError as e: + return {"error": str(e), "stderr": e.stderr} + finally: + Path(tmp_path).unlink(missing_ok=True) + +@mcp.tool() +def deseq2_get_normalized_counts( + counts_path: str, + metadata_path: str, + design_formula: str, + output_path: str, + normalization_type: str = "ratio" +): + """ + Calculates size factors and returns normalized counts. + + Args: + counts_path: Path to raw counts matrix. + metadata_path: Path to sample metadata. + design_formula: Design formula. + output_path: Path to save normalized counts CSV. + normalization_type: Method for size factor estimation ('ratio', 'poscounts', 'iterate'). + """ + counts_p = Path(counts_path) + meta_p = Path(metadata_path) + out_p = Path(output_path) + + r_script = f""" + library(DESeq2) + counts <- read.csv("{counts_p}", row.names=1, check.names=FALSE) + coldata <- read.csv("{meta_p}", row.names=1, check.names=FALSE) + dds <- DESeqDataSetFromMatrix(countData=counts, colData=coldata, design=as.formula("{design_formula}")) + dds <- estimateSizeFactors(dds, type="{normalization_type}") + norm_counts <- counts(dds, normalized=TRUE) + write.csv(norm_counts, file="{out_p}") + """ + + with tempfile.NamedTemporaryFile(suffix=".R", mode="w", delete=False) as tmp: + tmp.write(r_script) + tmp_path = tmp.name + + try: + cmd = ["Rscript", tmp_path] + result = subprocess.run(cmd, capture_output=True, text=True, check=True) + return { + "command_executed": " ".join(cmd), + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": [str(out_p)] + } + except subprocess.CalledProcessError as e: + return {"error": str(e), "stderr": e.stderr} + finally: + Path(tmp_path).unlink(missing_ok=True) + +@mcp.tool() +def deseq2_plot_pca( + counts_path: str, + metadata_path: str, + design_formula: str, + intgroup_columns: List[str], + output_plot_path: str, + ntop: int = 500 +): + """ + Generates a PCA plot from DESeq2 transformed data. + + Args: + counts_path: Path to raw counts matrix. + metadata_path: Path to sample metadata. + design_formula: Design formula. + intgroup_columns: List of columns in metadata to use for grouping/coloring. + output_plot_path: Path to save the PCA plot (PDF or PNG). + ntop: Number of top variable genes to use for PCA. + """ + counts_p = Path(counts_path) + meta_p = Path(metadata_path) + out_p = Path(output_plot_path) + + intgroup_r = 'c("' + '","'.join(intgroup_columns) + '")' + + r_script = f""" + library(DESeq2) + library(ggplot2) + counts <- read.csv("{counts_p}", row.names=1, check.names=FALSE) + coldata <- read.csv("{meta_p}", row.names=1, check.names=FALSE) + dds <- DESeqDataSetFromMatrix(countData=counts, colData=coldata, design=as.formula("{design_formula}")) + vsd <- vst(dds, blind=TRUE) + + pca_data <- plotPCA(vsd, intgroup={intgroup_r}, ntop={ntop}, returnData=TRUE) + percentVar <- round(100 * attr(pca_data, "percentVar")) + + p <- ggplot(pca_data, aes(PC1, PC2, color={intgroup_columns[0]})) + + geom_point(size=3) + + xlab(paste0("PC1: ", percentVar[1], "% variance")) + + ylab(paste0("PC2: ", percentVar[2], "% variance")) + + coord_fixed() + + theme_bw() + + ggsave("{out_p}", plot=p, device="{out_p.suffix[1:]}") + """ + + with tempfile.NamedTemporaryFile(suffix=".R", mode="w", delete=False) as tmp: + tmp.write(r_script) + tmp_path = tmp.name + + try: + cmd = ["Rscript", tmp_path] + result = subprocess.run(cmd, capture_output=True, text=True, check=True) + return { + "command_executed": " ".join(cmd), + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": [str(out_p)] + } + except subprocess.CalledProcessError as e: + return {"error": str(e), "stderr": e.stderr} + finally: + Path(tmp_path).unlink(missing_ok=True) \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-deseq2/app/bioconductor-deseq2_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-deseq2/app/bioconductor-deseq2_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..99d92238817537bd2f32a6a1d11231a7817d3dd8 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-deseq2/app/bioconductor-deseq2_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-deseq2/app/bioconductor-deseq2_server.py') +SERVER_NAME = 'biosci_bioconductor_deseq2' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-deseq2/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-deseq2/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-deseq2/app/requirements.txt @@ -0,0 +1 @@ + diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-deseq2/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-deseq2/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..44fca83c313aca169d4a672bc807c812c843473f --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-deseq2/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-bioconductor-deseq2: + build: . + image: mcp-bioconductor-deseq2:latest + container_name: mcp-bioconductor-deseq2 + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=bioconductor-deseq2 + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-deseq2/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-deseq2/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..6e0fbe21343a08899e814e52812d2bb7e94709bd --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-deseq2/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - bioconductor-deseq2 + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-deseq2/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-deseq2/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-deseq2/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-dnacopy/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-dnacopy/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..acfa6545a2f171d74ff198f82cd7aaba66db4989 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-dnacopy/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install bioconductor-dnacopy via conda (e.g., from bioconda) +RUN conda install -c bioconda bioconductor-dnacopy -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY app/bioconductor-dnacopy_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/bioconductor-dnacopy_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/bioconductor-dnacopy_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-dnacopy/app/bioconductor-dnacopy_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-dnacopy/app/bioconductor-dnacopy_server.py new file mode 100644 index 0000000000000000000000000000000000000000..8a1aa7e2b77094a03372e2ea22b29a69aec3434b --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-dnacopy/app/bioconductor-dnacopy_server.py @@ -0,0 +1,371 @@ +import subprocess +import tempfile +from pathlib import Path +from typing import Optional, List, Union + +@mcp.tool() +def dnacopy_segment( + input_file: str, + output_file: str, + chrom_col: str, + maploc_col: str, + sample_col: str, + data_type: str = "logratio", + alpha: float = 0.01, + nperm: int = 10000, + p_method: str = "hybrid", + min_width: int = 2, + kmax: int = 25, + nmin: int = 200, + eta: float = 0.05, + trim: float = 0.025, + undo_splits: str = "none", + undo_pr: float = 0.05, + undo_sd: float = 3.0, + verbose: int = 1, + sep: str = "\t" +): + """ + Perform Circular Binary Segmentation (CBS) on DNA copy number data using the DNAcopy Bioconductor package. + + Args: + input_file: Path to the input data file (TSV/CSV). + output_file: Path where the segmented output will be saved. + chrom_col: Name of the column containing chromosome information. + maploc_col: Name of the column containing genomic coordinates/map locations. + sample_col: Name of the column containing the copy number log-ratio data. + data_type: Type of data, either 'logratio' or 'binary'. + alpha: Significance level for the test to accept change-points. + nperm: Number of permutations used for p-value computation. + p_method: Method used for p-value computation ('hybrid' or 'perm'). + min_width: Minimum number of markers for a segment. + kmax: Maximum width of smaller segment for the hybrid method. + nmin: Minimum length of data for which the hybrid method is used. + eta: Probability threshold for the hybrid method. + trim: Proportion of data to be trimmed for calculating the smoothed variance. + undo_splits: Method to undo unnecessary splits ('none', 'prune', or 'sdundo'). + undo_pr: Significance level for pruning (if undo_splits='prune'). + undo_sd: Number of standard deviations for undoing (if undo_splits='sdundo'). + verbose: Level of verbosity (0, 1, 2). + sep: Field separator in the input file (default is tab). + """ + # Input validation + input_path = Path(input_file) + if not input_path.exists(): + return {"error": f"Input file not found: {input_file}"} + + if data_type not in ["logratio", "binary"]: + return {"error": "data_type must be 'logratio' or 'binary'"} + + if p_method not in ["hybrid", "perm"]: + return {"error": "p_method must be 'hybrid' or 'perm'"} + + if undo_splits not in ["none", "prune", "sdundo"]: + return {"error": "undo_splits must be 'none', 'prune', or 'sdundo'"} + + # Create R script + r_script_content = f""" + library(DNAcopy) + + # Load data + data_df <- read.table("{input_file}", header=TRUE, sep="{sep}", check.names=FALSE) + + # Create CNA object + cna_obj <- CNA( + genomdat = data_df[["{sample_col}"]], + chrom = data_df[["{chrom_col}"]], + maploc = data_df[["{maploc_col}"]], + data.type = "{data_type}", + sampleid = "{sample_col}" + ) + + # Run segmentation + segmented <- segment( + cna_obj, + alpha = {alpha}, + nperm = {nperm}, + p.method = "{p_method}", + min.width = {min_width}, + kmax = {kmax}, + nmin = {nmin}, + eta = {eta}, + trim = {trim}, + undo.splits = "{undo_splits}", + undo.PR = {undo_pr}, + undo.SD = {undo_sd}, + verbose = {verbose} + ) + + # Write output + write.table(segmented$output, file="{output_file}", sep="\\t", quote=FALSE, row.names=FALSE) + """ + + try: + with tempfile.NamedTemporaryFile(mode='w', suffix='.R', delete=False) as tmp: + tmp.write(r_script_content) + tmp_path = tmp.name + + result = subprocess.run( + ["Rscript", tmp_path], + capture_output=True, + text=True, + check=True + ) + + Path(tmp_path).unlink() # Clean up + + return { + "command_executed": f"Rscript {tmp_path} (DNAcopy::segment)", + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": [output_file] + } + except subprocess.CalledProcessError as e: + return { + "error": "R execution failed", + "stdout": e.stdout, + "stderr": e.stderr + } + except Exception as e: + return {"error": str(e)} + +@mcp.tool() +def dnacopy_smooth( + input_file: str, + output_file: str, + chrom_col: str, + maploc_col: str, + sample_col: str, + smooth_region: int = 10, + outlier_sd_scale: float = 4.0, + smooth_sd_scale: float = 2.0, + trim: float = 0.025, + sep: str = "\t" +): + """ + Smooth DNA copy number data to detect and mitigate outliers before segmentation. + + Args: + input_file: Path to the input data file (TSV/CSV). + output_file: Path where the smoothed data will be saved. + chrom_col: Name of the column containing chromosome information. + maploc_col: Name of the column containing genomic coordinates. + sample_col: Name of the column containing the copy number log-ratio data. + smooth_region: Number of points to the left and right of a point to use for smoothing. + outlier_sd_scale: The number of SDs from the local mean to consider a point an outlier. + smooth_sd_scale: The number of SDs from the local mean to which an outlier is smoothed. + trim: Proportion of data to be trimmed for calculating the smoothed variance. + sep: Field separator in the input file. + """ + input_path = Path(input_file) + if not input_path.exists(): + return {"error": f"Input file not found: {input_file}"} + + r_script_content = f""" + library(DNAcopy) + + data_df <- read.table("{input_file}", header=TRUE, sep="{sep}", check.names=FALSE) + + cna_obj <- CNA( + genomdat = data_df[["{sample_col}"]], + chrom = data_df[["{chrom_col}"]], + maploc = data_df[["{maploc_col}"]], + sampleid = "{sample_col}" + ) + + smoothed_obj <- smooth.CNA( + cna_obj, + smooth.region = {smooth_region}, + outlier.SD.scale = {outlier_sd_scale}, + smooth.SD.scale = {smooth_sd_scale}, + trim = {trim} + ) + + # Reconstruct dataframe for output + out_df <- data.frame( + chrom = smoothed_obj$chrom, + maploc = smoothed_obj$maploc, + smoothed_val = smoothed_obj[[3]] + ) + colnames(out_df) <- c("{chrom_col}", "{maploc_col}", "{sample_col}") + + write.table(out_df, file="{output_file}", sep="\\t", quote=FALSE, row.names=FALSE) + """ + + try: + with tempfile.NamedTemporaryFile(mode='w', suffix='.R', delete=False) as tmp: + tmp.write(r_script_content) + tmp_path = tmp.name + + result = subprocess.run( + ["Rscript", tmp_path], + capture_output=True, + text=True, + check=True + ) + + Path(tmp_path).unlink() + + return { + "command_executed": f"Rscript {tmp_path} (DNAcopy::smooth.CNA)", + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": [output_file] + } + except subprocess.CalledProcessError as e: + return {"error": "R execution failed", "stdout": e.stdout, "stderr": e.stderr} + except Exception as e: + return {"error": str(e)} + +@mcp.tool() +def dnacopy_plot( + input_file: str, + plot_file: str, + chrom_col: str, + maploc_col: str, + sample_col: str, + plot_type: str = "whole", + xmaploc: bool = False, + sep: str = "\t" +): + """ + Generate plots for DNA copy number segmentation results. + + Args: + input_file: Path to the input data file (TSV/CSV). + plot_file: Path where the plot (PNG) will be saved. + chrom_col: Name of the column containing chromosome information. + maploc_col: Name of the column containing genomic coordinates. + sample_col: Name of the column containing the copy number log-ratio data. + plot_type: Type of plot ('whole', 'chromosomal', or 'sample'). + xmaploc: Logical flag to indicate if the x-axis should be map location (True) or index (False). + sep: Field separator in the input file. + """ + input_path = Path(input_file) + if not input_path.exists(): + return {"error": f"Input file not found: {input_file}"} + + if not plot_file.lower().endswith('.png'): + plot_file += ".png" + + r_script_content = f""" + library(DNAcopy) + + data_df <- read.table("{input_file}", header=TRUE, sep="{sep}", check.names=FALSE) + + cna_obj <- CNA( + genomdat = data_df[["{sample_col}"]], + chrom = data_df[["{chrom_col}"]], + maploc = data_df[["{maploc_col}"]], + sampleid = "{sample_col}" + ) + + segmented <- segment(cna_obj, verbose=0) + + png("{plot_file}", width=1200, height=800) + plot(segmented, plot.type="{plot_type}", xmaploc={"TRUE" if xmaploc else "FALSE"}) + dev.off() + """ + + try: + with tempfile.NamedTemporaryFile(mode='w', suffix='.R', delete=False) as tmp: + tmp.write(r_script_content) + tmp_path = tmp.name + + result = subprocess.run( + ["Rscript", tmp_path], + capture_output=True, + text=True, + check=True + ) + + Path(tmp_path).unlink() + + return { + "command_executed": f"Rscript {tmp_path} (DNAcopy::plot)", + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": [plot_file] + } + except subprocess.CalledProcessError as e: + return {"error": "R execution failed", "stdout": e.stdout, "stderr": e.stderr} + except Exception as e: + return {"error": str(e)} + +@mcp.tool() +def dnacopy_subset( + input_file: str, + output_file: str, + chrom_col: str, + maploc_col: str, + sample_col: str, + chromosomes: List[str], + sep: str = "\t" +): + """ + Subset a CNA object by chromosomes using DNAcopy internal logic. + + Args: + input_file: Path to the input data file. + output_file: Path where the subsetted data will be saved. + chrom_col: Name of the chromosome column. + maploc_col: Name of the map location column. + sample_col: Name of the sample data column. + chromosomes: List of chromosome names to keep (e.g., ["1", "2", "X"]). + sep: Field separator. + """ + input_path = Path(input_file) + if not input_path.exists(): + return {"error": f"Input file not found: {input_file}"} + + chrom_list_r = 'c("' + '", "'.join(chromosomes) + '")' + + r_script_content = f""" + library(DNAcopy) + + data_df <- read.table("{input_file}", header=TRUE, sep="{sep}", check.names=FALSE) + + cna_obj <- CNA( + genomdat = data_df[["{sample_col}"]], + chrom = data_df[["{chrom_col}"]], + maploc = data_df[["{maploc_col}"]], + sampleid = "{sample_col}" + ) + + subset_obj <- subset(cna_obj, chromlist = {chrom_list_r}) + + # Reconstruct dataframe + out_df <- data.frame( + chrom = subset_obj$chrom, + maploc = subset_obj$maploc, + val = subset_obj[[3]] + ) + colnames(out_df) <- c("{chrom_col}", "{maploc_col}", "{sample_col}") + + write.table(out_df, file="{output_file}", sep="\\t", quote=FALSE, row.names=FALSE) + """ + + try: + with tempfile.NamedTemporaryFile(mode='w', suffix='.R', delete=False) as tmp: + tmp.write(r_script_content) + tmp_path = tmp.name + + result = subprocess.run( + ["Rscript", tmp_path], + capture_output=True, + text=True, + check=True + ) + + Path(tmp_path).unlink() + + return { + "command_executed": f"Rscript {tmp_path} (DNAcopy::subset)", + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": [output_file] + } + except subprocess.CalledProcessError as e: + return {"error": "R execution failed", "stdout": e.stdout, "stderr": e.stderr} + except Exception as e: + return {"error": str(e)} \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-dnacopy/app/bioconductor-dnacopy_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-dnacopy/app/bioconductor-dnacopy_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..85644ebdb44a39809845a2eb4ec615d751fb9cec --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-dnacopy/app/bioconductor-dnacopy_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-dnacopy/app/bioconductor-dnacopy_server.py') +SERVER_NAME = 'biosci_bioconductor_dnacopy' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-dnacopy/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-dnacopy/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-dnacopy/app/requirements.txt @@ -0,0 +1 @@ + diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-dnacopy/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-dnacopy/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..2a3bc04cf7735b56b19adb7824aab027ad1edc38 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-dnacopy/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-bioconductor-dnacopy: + build: . + image: mcp-bioconductor-dnacopy:latest + container_name: mcp-bioconductor-dnacopy + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=bioconductor-dnacopy + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-dnacopy/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-dnacopy/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..a899be3d3c4b68ef4f21429190feaaf5b44d4dbf --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-dnacopy/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - bioconductor-dnacopy + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-dnacopy/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-dnacopy/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-dnacopy/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-erma/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-erma/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..1f7b43050805f6bea8ccb79abf39b082b62f9ad5 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-erma/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install bioconductor-erma via conda (e.g., from bioconda) +RUN conda install -c bioconda bioconductor-erma -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY app/bioconductor-erma_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/bioconductor-erma_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/bioconductor-erma_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-erma/app/bioconductor-erma_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-erma/app/bioconductor-erma_server.py new file mode 100644 index 0000000000000000000000000000000000000000..427b357057822d1a7dc9899e89a14e53e36a25ad --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-erma/app/bioconductor-erma_server.py @@ -0,0 +1,108 @@ +import subprocess +import logging +from pathlib import Path +from typing import List, Optional + +# Configure basic logging +logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') + +# This is a placeholder for the MCP decorator. +# In a real MCP environment, this would be provided by the MCP framework. +class mcp: + @staticmethod + def tool(): + def decorator(func): + return func + return decorator + +@mcp.tool() +def run_erma_script( + script_path: Path, + output_dir: Path, + args: Optional[List[str]] = None, +): + """ + Executes a user-provided R script in an environment with bioconductor-erma. + + The 'bioconductor-erma' package is an R library for epigenomic data analysis + and does not provide a direct command-line interface. This tool serves as a + generic wrapper to execute R scripts that utilize the 'erma' library. The + user's script is responsible for implementing the analysis logic and writing + any output files to the specified directory. + + Args: + script_path: The path to the R script to be executed. + output_dir: The path to the directory where the R script is expected to + write its output files. This directory will be created if it + does not exist. + args: An optional list of command-line arguments to be passed to the + R script. + """ + # 1. Input Validation + if not script_path.is_file(): + raise FileNotFoundError(f"The specified R script does not exist: {script_path}") + + # 2. File Path Handling + try: + output_dir.mkdir(parents=True, exist_ok=True) + except OSError as e: + logging.error(f"Failed to create output directory {output_dir}: {e}") + raise + + # 3. Command Construction + command = ["Rscript", "--vanilla", str(script_path)] + if args: + command.extend([str(arg) for arg in args]) + + command_executed = " ".join(command) + logging.info(f"Executing command: {command_executed}") + + # 4. Subprocess Execution and Error Handling + try: + result = subprocess.run( + command, + capture_output=True, + text=True, + check=True, + cwd=output_dir, # Execute the script from the output directory + ) + stdout = result.stdout + stderr = result.stderr + logging.info("R script executed successfully.") + + except FileNotFoundError: + error_message = "Error: 'Rscript' command not found. Please ensure R is installed and accessible in the system's PATH." + logging.error(error_message) + return { + "command_executed": command_executed, + "stdout": "", + "stderr": error_message, + "output_files": [] + } + + except subprocess.CalledProcessError as e: + logging.error(f"R script failed with exit code {e.returncode}.") + logging.error(f"STDOUT: {e.stdout}") + logging.error(f"STDERR: {e.stderr}") + return { + "command_executed": command_executed, + "stdout": e.stdout, + "stderr": e.stderr, + "output_files": [] + } + + # 5. Structured Result Return + try: + output_files = [str(f) for f in output_dir.iterdir() if f.is_file()] + except OSError as e: + logging.error(f"Error listing output files in {output_dir}: {e}") + # Append this error to stderr to inform the user about the issue + stderr += f"\n[MCP Wrapper Error] Could not list output files: {e}" + output_files = [] + + return { + "command_executed": command_executed, + "stdout": stdout, + "stderr": stderr, + "output_files": output_files + } \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-erma/app/bioconductor-erma_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-erma/app/bioconductor-erma_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..fe405693464562daf5e6d6fb4b03f084a232a896 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-erma/app/bioconductor-erma_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-erma/app/bioconductor-erma_server.py') +SERVER_NAME = 'biosci_bioconductor_erma' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-erma/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-erma/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-erma/app/requirements.txt @@ -0,0 +1 @@ + diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-erma/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-erma/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..782d3f8e2e0c9fa5dcda40aff6c86fe72ffa05ae --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-erma/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-bioconductor-erma: + build: . + image: mcp-bioconductor-erma:latest + container_name: mcp-bioconductor-erma + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=bioconductor-erma + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-erma/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-erma/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..e6edd0dc0eba1149262c0edeed5f97fce8950fde --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-erma/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - bioconductor-erma + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-erma/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-erma/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-erma/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-escher/app/bioconductor-escher_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-escher/app/bioconductor-escher_server.py new file mode 100644 index 0000000000000000000000000000000000000000..05b291bf2ca2d8d4cf530465194a10c4262ed641 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-escher/app/bioconductor-escher_server.py @@ -0,0 +1,279 @@ +import subprocess +import tempfile +from pathlib import Path +from typing import List, Optional + +# MCP-decorated functions for the 'escher' command-line tool. +# Note: This implementation is for the Python 'escher' package, which provides a +# command-line interface. The 'bioconductor-escher' package is an R wrapper +# and does not have a corresponding command-line tool. It is assumed the +# Python tool was the intended target. + + +@mcp.tool() +def escher_convert( + input_files: List[Path], + output: Optional[Path] = None, + format: Optional[str] = None, +): + """ + Converts Escher maps (JSON) to SVG or PNG formats. + + This tool wraps the 'escher convert' command. + + Args: + input_files: A list of one or more Escher map files (JSON) to convert. + output: The output file or directory. If not provided, output is saved + next to the input files. If multiple inputs are given, this must + be a directory. + format: The output format ('svg' or 'png'). Overrides the output file + extension. Defaults to 'svg' if not determinable from output path. + """ + # Input validation + if not input_files: + raise ValueError("At least one input file must be provided.") + for file_path in input_files: + if not file_path.is_file(): + raise FileNotFoundError(f"Input file not found: {file_path}") + + if format and format not in ["svg", "png"]: + raise ValueError("Invalid format. Must be 'svg' or 'png'.") + + if len(input_files) > 1 and output and output.is_file(): + raise ValueError( + "When multiple input files are provided, the output path must be a directory, not a file." + ) + + command = ["escher", "convert"] + command.extend(["--input", *[str(p) for p in input_files]]) + + output_format = "svg" + if format: + command.extend(["--format", format]) + output_format = format + elif output and output.suffix in [".svg", ".png"]: + output_format = output.suffix[1:] + + output_paths = [] + if output: + command.extend(["--output", str(output)]) + # Check if output is intended to be a directory + if output.is_dir() or (len(input_files) > 1) or (not output.suffix): + output.mkdir(parents=True, exist_ok=True) + for in_file in input_files: + output_paths.append(output / f"{in_file.stem}.{output_format}") + else: + # Output is a file + output.parent.mkdir(parents=True, exist_ok=True) + output_paths.append(output) + else: + # No output specified, save next to input + for in_file in input_files: + output_paths.append(in_file.with_suffix(f".{output_format}")) + + try: + process = subprocess.run( + command, + check=True, + capture_output=True, + text=True, + ) + return { + "command_executed": " ".join(command), + "stdout": process.stdout, + "stderr": process.stderr, + "output_files": [str(p) for p in output_paths if p.exists()], + } + except FileNotFoundError: + raise RuntimeError( + "Error: 'escher' command not found. Please ensure the escher package is installed and in your PATH." + ) + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(command), + "stdout": e.stdout, + "stderr": e.stderr, + "output_files": [], + } + + +@mcp.tool() +def escher_clear_cache(): + """ + Clears the Escher cache of downloaded maps and models. + + This tool wraps the 'escher clear-cache' command. + """ + command = ["escher", "clear-cache"] + + try: + process = subprocess.run( + command, + check=True, + capture_output=True, + text=True, + ) + return { + "command_executed": " ".join(command), + "stdout": process.stdout, + "stderr": process.stderr, + } + except FileNotFoundError: + raise RuntimeError( + "Error: 'escher' command not found. Please ensure the escher package is installed and in your PATH." + ) + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(command), + "stdout": e.stdout, + "stderr": e.stderr, + } + + +@mcp.tool() +def escher_run( + port: int = 7778, + host: str = "127.0.0.1", + no_cache: bool = False, + timeout: int = 30, +): + """ + Runs a new Escher server. + + Warning: This command starts a long-running server process. The 'timeout' + parameter is used to terminate the server after a specified duration. + This tool is primarily for interactive use and may have limited utility + in an automated MCP environment. The --no-browser flag is always used. + + Args: + port: The port to run the server on. + host: The host for the server. + no_cache: If True, do not cache Escher resources. + timeout: The maximum time in seconds to run the server before terminating. + """ + if timeout <= 0: + raise ValueError("Timeout must be a positive integer.") + + command = ["escher", "run", "--no-browser"] + command.extend(["--port", str(port)]) + command.extend(["--host", host]) + if no_cache: + command.append("--no-cache") + + try: + process = subprocess.run( + command, capture_output=True, text=True, timeout=timeout + ) + # This block is unlikely to be reached as the server runs indefinitely + return { + "command_executed": " ".join(command), + "stdout": process.stdout, + "stderr": process.stderr, + } + except FileNotFoundError: + raise RuntimeError( + "Error: 'escher' command not found. Please ensure the escher package is installed and in your PATH." + ) + except subprocess.TimeoutExpired as e: + # This is the expected outcome for this tool in an MCP environment + return { + "command_executed": " ".join(command), + "stdout": e.stdout or "", + "stderr": e.stderr or f"Process terminated after {timeout}s timeout.", + } + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(command), + "stdout": e.stdout, + "stderr": e.stderr, + } + + +@mcp.tool() +def escher_launch( + map_name: Optional[str] = None, + map_json: Optional[Path] = None, + model_name: Optional[str] = None, + model_json: Optional[Path] = None, + port: int = 7779, + host: str = "127.0.0.1", + no_cache: bool = False, + never_ask_before_quit: bool = False, + timeout: int = 30, +): + """ + Launches an Escher instance in a server, optionally loading a map/model. + + Warning: This command starts a long-running server process. The 'timeout' + parameter is used to terminate the server after a specified duration. + This tool is primarily for interactive use and may have limited utility + in an automated MCP environment. The --no-browser flag is always used. + + Args: + map_name: The name of the map to load from the Escher server. + map_json: A local JSON file for the map. + model_name: The name of the model to load from the Escher server. + model_json: A local JSON file for the model. + port: The port to run the server on. + host: The host for the server. + no_cache: If True, do not cache Escher resources. + never_ask_before_quit: If True, never ask "Are you sure you want to quit?". + timeout: The maximum time in seconds to run the server before terminating. + """ + # Input validation + if map_name and map_json: + raise ValueError("'map_name' and 'map_json' are mutually exclusive.") + if model_name and model_json: + raise ValueError("'model_name' and 'model_json' are mutually exclusive.") + if map_json and not map_json.is_file(): + raise FileNotFoundError(f"Map JSON file not found: {map_json}") + if model_json and not model_json.is_file(): + raise FileNotFoundError(f"Model JSON file not found: {model_json}") + if timeout <= 0: + raise ValueError("Timeout must be a positive integer.") + + command = ["escher", "launch", "--no-browser"] + + if map_name: + command.extend(["--map_name", map_name]) + if map_json: + command.extend(["--map_json", str(map_json)]) + if model_name: + command.extend(["--model_name", model_name]) + if model_json: + command.extend(["--model_json", str(model_json)]) + + command.extend(["--port", str(port)]) + command.extend(["--host", host]) + + if no_cache: + command.append("--no-cache") + if never_ask_before_quit: + command.append("--never_ask_before_quit") + + try: + process = subprocess.run( + command, capture_output=True, text=True, timeout=timeout + ) + return { + "command_executed": " ".join(command), + "stdout": process.stdout, + "stderr": process.stderr, + } + except FileNotFoundError: + raise RuntimeError( + "Error: 'escher' command not found. Please ensure the escher package is installed and in your PATH." + ) + except subprocess.TimeoutExpired as e: + # This is the expected outcome for this tool in an MCP environment + return { + "command_executed": " ".join(command), + "stdout": e.stdout or "", + "stderr": e.stderr or f"Process terminated after {timeout}s timeout.", + } + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(command), + "stdout": e.stdout, + "stderr": e.stderr, + } \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-escher/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-escher/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-escher/app/requirements.txt @@ -0,0 +1 @@ + diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-escher/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-escher/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..de7a0181d2217e0800a6feab0783313d926346ec --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-escher/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-bioconductor-escher: + build: . + image: mcp-bioconductor-escher:latest + container_name: mcp-bioconductor-escher + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=bioconductor-escher + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-genomeinfodb/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-genomeinfodb/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..da1928863b64adf79436afc2d7c96a25511e3b41 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-genomeinfodb/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install bioconductor-genomeinfodb via conda (e.g., from bioconda) +RUN conda install -c bioconda bioconductor-genomeinfodb -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY app/bioconductor-genomeinfodb_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/bioconductor-genomeinfodb_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/bioconductor-genomeinfodb_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-genomeinfodb/app/bioconductor-genomeinfodb_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-genomeinfodb/app/bioconductor-genomeinfodb_server.py new file mode 100644 index 0000000000000000000000000000000000000000..247ad74c244354ac05b0ea362a4966374284ace9 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-genomeinfodb/app/bioconductor-genomeinfodb_server.py @@ -0,0 +1,325 @@ +import subprocess +import tempfile +import shutil +from pathlib import Path +from typing import Optional, List, Dict +import sys +import logging + +# Configure logging +logging.basicConfig(level=logging.INFO) +logger = logging.getLogger(__name__) + +# Mock the mcp.tool decorator for standalone execution +class mcp: + def tool(func): + return func + +def _check_rscript(): + """Checks if Rscript is available in the system PATH.""" + if not shutil.which("Rscript"): + raise EnvironmentError( + "Rscript not found in PATH. R and the GenomeInfoDb Bioconductor " + "package are required to run these tools." + ) + +@mcp.tool +def genomeinfodb_list_styles(species: str) -> Dict: + """ + Lists the available chromosome naming styles (e.g., UCSC, NCBI) for a given species. + + This tool wraps the `genomeStyles()` function from the R/Bioconductor GenomeInfoDb package. + + Args: + species: The scientific name of the species (e.g., "Homo sapiens", "Mus musculus"). + + Returns: + A dictionary containing the command executed, stdout (with the list of styles), + stderr, and a list of available styles. + """ + _check_rscript() + if not species: + raise ValueError("The 'species' argument cannot be empty.") + + r_script_content = f""" + suppressPackageStartupMessages(library(GenomeInfoDb)) + tryCatch({{ + styles <- genomeStyles('{species}') + if (length(styles) == 0) {{ + cat("No styles found for species: {species}\\n", file=stderr()) + }} else {{ + cat(paste(names(styles), collapse='\\n')) + }} + }}, error = function(e) {{ + cat(paste("Error:", e$message, "\\n"), file=stderr()) + quit(status = 1) + }}) + """ + + command = ["Rscript", "-e", r_script_content] + command_executed = " ".join(command) + logger.info(f"Executing command: {command_executed}") + + try: + result = subprocess.run( + command, + capture_output=True, + text=True, + check=True, + ) + styles_list = result.stdout.strip().split('\n') if result.stdout.strip() else [] + return { + "command_executed": command_executed, + "stdout": result.stdout, + "stderr": result.stderr, + "styles": styles_list, + } + except subprocess.CalledProcessError as e: + logger.error(f"Error executing GenomeInfoDb R script: {e.stderr}") + return { + "command_executed": command_executed, + "stdout": e.stdout, + "stderr": e.stderr, + "error": "Failed to list genome styles. Check if the species name is correct and GenomeInfoDb is installed.", + } + +@mcp.tool +def genomeinfodb_map_seqlevels( + target_style: str, + species: str, + input_file: Path, + output_file: Path, +) -> Dict: + """ + Maps chromosome names from an input file to a specified naming convention (style). + + This tool wraps the `mapSeqlevels()` function from the R/Bioconductor GenomeInfoDb package. + It produces a two-column TSV file: 'original' and 'mapped'. + + Args: + target_style: The target naming style (e.g., "UCSC", "NCBI", "Ensembl"). + species: The scientific name of the species (e.g., "Homo sapiens"). + input_file: Path to a file containing one chromosome name per line. + output_file: Path to write the output TSV file with original and mapped names. + + Returns: + A dictionary containing the command executed, stdout, stderr, and the path to the output file. + """ + _check_rscript() + if not target_style: + raise ValueError("The 'target_style' argument cannot be empty.") + if not species: + raise ValueError("The 'species' argument cannot be empty.") + if not input_file.is_file(): + raise FileNotFoundError(f"Input file not found: {input_file}") + + output_file.parent.mkdir(parents=True, exist_ok=True) + + r_script_content = f""" + suppressPackageStartupMessages(library(GenomeInfoDb)) + args <- commandArgs(trailingOnly=TRUE) + inputFile <- args[1] + targetStyle <- args[2] + species <- args[3] + outputFile <- args[4] + + tryCatch({{ + seqs <- readLines(inputFile) + if (length(seqs) == 0) {{ + stop("Input file is empty.") + }} + mapped_seqs <- mapSeqlevels(seqs, style=targetStyle, species=species) + output_df <- data.frame(original=seqs, mapped=mapped_seqs) + write.table(output_df, file=outputFile, sep='\\t', row.names=FALSE, col.names=TRUE, quote=FALSE) + }}, error = function(e) {{ + cat(paste("Error:", e$message, "\\n"), file=stderr()) + quit(status = 1) + }}) + """ + + command = [ + "Rscript", + "-e", + r_script_content, + str(input_file), + target_style, + species, + str(output_file), + ] + command_executed = " ".join(command) + logger.info(f"Executing command: {command_executed}") + + try: + result = subprocess.run( + command, + capture_output=True, + text=True, + check=True, + ) + return { + "command_executed": command_executed, + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": {"mapped_seqlevels": str(output_file)}, + } + except subprocess.CalledProcessError as e: + logger.error(f"Error executing GenomeInfoDb R script: {e.stderr}") + return { + "command_executed": command_executed, + "stdout": e.stdout, + "stderr": e.stderr, + "error": "Failed to map seqlevels. Check parameters and ensure input file is correctly formatted.", + } + +@mcp.tool +def genomeinfodb_get_seqinfo( + genome: str, + output_file: Path, +) -> Dict: + """ + Fetches sequence information (lengths, circularity) for a given genome assembly. + + This tool wraps the `Seqinfo()` constructor from the R/Bioconductor GenomeInfoDb package. + It produces a TSV file containing seqnames, seqlengths, isCircular, and genome. + + Args: + genome: The genome assembly identifier (e.g., "hg19", "hg38", "mm10"). + output_file: Path to write the output TSV file with sequence information. + + Returns: + A dictionary containing the command executed, stdout, stderr, and the path to the output file. + """ + _check_rscript() + if not genome: + raise ValueError("The 'genome' argument cannot be empty.") + + output_file.parent.mkdir(parents=True, exist_ok=True) + + r_script_content = f""" + suppressPackageStartupMessages(library(GenomeInfoDb)) + args <- commandArgs(trailingOnly=TRUE) + genome_id <- args[1] + outputFile <- args[2] + + tryCatch({{ + si <- Seqinfo(genome=genome_id) + si_df <- as.data.frame(si) + write.table(si_df, file=outputFile, sep='\\t', row.names=TRUE, col.names=NA, quote=FALSE) + }}, error = function(e) {{ + cat(paste("Error:", e$message, "\\n"), file=stderr()) + quit(status = 1) + }}) + """ + + command = ["Rscript", "-e", r_script_content, genome, str(output_file)] + command_executed = " ".join(command) + logger.info(f"Executing command: {command_executed}") + + try: + result = subprocess.run( + command, + capture_output=True, + text=True, + check=True, + ) + return { + "command_executed": command_executed, + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": {"seqinfo": str(output_file)}, + } + except subprocess.CalledProcessError as e: + logger.error(f"Error executing GenomeInfoDb R script: {e.stderr}") + return { + "command_executed": command_executed, + "stdout": e.stdout, + "stderr": e.stderr, + "error": "Failed to get seqinfo. Check if the genome identifier is correct.", + } + +@mcp.tool +def genomeinfodb_rename_seqlevels( + input_file: Path, + mapping_file: Path, + output_file: Path, +) -> Dict: + """ + Renames chromosome names from an input file based on a provided mapping file. + + This tool wraps the `renameSeqlevels()` function from the R/Bioconductor GenomeInfoDb package. + It produces a two-column TSV file: 'original' and 'renamed'. + + Args: + input_file: Path to a file containing one chromosome name per line. + mapping_file: Path to a two-column TSV file (old_namenew_name) without a header. + output_file: Path to write the output TSV file with original and renamed names. + + Returns: + A dictionary containing the command executed, stdout, stderr, and the path to the output file. + """ + _check_rscript() + if not input_file.is_file(): + raise FileNotFoundError(f"Input file not found: {input_file}") + if not mapping_file.is_file(): + raise FileNotFoundError(f"Mapping file not found: {mapping_file}") + + output_file.parent.mkdir(parents=True, exist_ok=True) + + r_script_content = f""" + suppressPackageStartupMessages(library(GenomeInfoDb)) + args <- commandArgs(trailingOnly=TRUE) + inputFile <- args[1] + mappingFile <- args[2] + outputFile <- args[3] + + tryCatch({{ + seqs <- readLines(inputFile) + if (length(seqs) == 0) {{ + stop("Input file is empty.") + }} + mapping_df <- read.table(mappingFile, sep='\\t', header=FALSE, stringsAsFactors=FALSE, col.names=c("old", "new")) + if (nrow(mapping_df) == 0) {{ + stop("Mapping file is empty.") + }} + value <- setNames(mapping_df$new, mapping_df$old) + renamed_seqs <- renameSeqlevels(seqs, value) + output_df <- data.frame(original=seqs, renamed=renamed_seqs) + write.table(output_df, file=outputFile, sep='\\t', row.names=FALSE, col.names=TRUE, quote=FALSE) + }}, error = function(e) {{ + cat(paste("Error:", e$message, "\\n"), file=stderr()) + quit(status = 1) + }}) + """ + + command = [ + "Rscript", + "-e", + r_script_content, + str(input_file), + str(mapping_file), + str(output_file), + ] + command_executed = " ".join(command) + logger.info(f"Executing command: {command_executed}") + + try: + result = subprocess.run( + command, + capture_output=True, + text=True, + check=True, + ) + return { + "command_executed": command_executed, + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": {"renamed_seqlevels": str(output_file)}, + } + except subprocess.CalledProcessError as e: + logger.error(f"Error executing GenomeInfoDb R script: {e.stderr}") + return { + "command_executed": command_executed, + "stdout": e.stdout, + "stderr": e.stderr, + "error": "Failed to rename seqlevels. Check file formats and content.", + } \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-genomeinfodb/app/bioconductor-genomeinfodb_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-genomeinfodb/app/bioconductor-genomeinfodb_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..2534a7528d190408439f3c6107dda8b95f1d6e4e --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-genomeinfodb/app/bioconductor-genomeinfodb_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-genomeinfodb/app/bioconductor-genomeinfodb_server.py') +SERVER_NAME = 'biosci_bioconductor_genomeinfodb' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-genomeinfodb/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-genomeinfodb/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-genomeinfodb/app/requirements.txt @@ -0,0 +1 @@ + diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-genomeinfodb/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-genomeinfodb/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..f45008fc4f9db6ed1b6aafd4d5c65aa0c5ac8c66 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-genomeinfodb/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-bioconductor-genomeinfodb: + build: . + image: mcp-bioconductor-genomeinfodb:latest + container_name: mcp-bioconductor-genomeinfodb + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=bioconductor-genomeinfodb + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-genomeinfodb/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-genomeinfodb/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..9340ec8e0823c9ed7de8c928916c2c625108234e --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-genomeinfodb/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - bioconductor-genomeinfodb + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-geomxtools/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-geomxtools/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..e7d23553fd16ce1626a84fcd3ab533e663d65307 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-geomxtools/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install bioconductor-geomxtools via conda (e.g., from bioconda) +RUN conda install -c bioconda bioconductor-geomxtools -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY app/bioconductor-geomxtools_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/bioconductor-geomxtools_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/bioconductor-geomxtools_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-geomxtools/app/bioconductor-geomxtools_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-geomxtools/app/bioconductor-geomxtools_server.py new file mode 100644 index 0000000000000000000000000000000000000000..7938087765354a4f310559016a2809375e7f9960 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-geomxtools/app/bioconductor-geomxtools_server.py @@ -0,0 +1,680 @@ +import subprocess +import tempfile +from pathlib import Path +from typing import Optional, List + +# In a real MCP environment, this would be imported. +# from mcp import tool as mcp_tool +# For this exercise, we assume the decorator @mcp.tool() is available. +class mcp: + @staticmethod + def tool(): + def decorator(func): + return func + return decorator + +@mcp.tool() +def read_nanostring_geomx_set( + dcc_files_dir: Path, + pkc_files: List[Path], + pheno_data_file: Path, + output_rds_path: Path, + pheno_data_sheet: Optional[str] = None, + pheno_data_dcc_col_name: str = "Sample_ID", + protocol_data_col_names: Optional[List[str]] = None, + experiment_data_col_names: Optional[List[str]] = None, +) -> dict: + """ + Reads NanoString GeoMx files (DCC, PKC, annotation) and creates a NanoStringGeomxSet object. + + This tool is a wrapper around the `readNanoStringGeoMxSet` function from the + R/Bioconductor package `GeomxTools`. It processes raw data into a structured + R object for downstream analysis. + + Args: + dcc_files_dir: Path to the directory containing DCC files. + pkc_files: A list of paths to PKC files. + pheno_data_file: Path to the sample annotation file (e.g., an Excel file). + output_rds_path: Path for the output RDS file which will contain the NanoStringGeomxSet object. + pheno_data_sheet: Optional name of the worksheet in the Excel annotation file. + pheno_data_dcc_col_name: Column name in the annotation file that matches DCC file names. + protocol_data_col_names: Optional list of column names in annotation to be added to protocolData. + experiment_data_col_names: Optional list of column names in annotation to be added to experimentData. + + Returns: + A dictionary containing the execution command, stdout, stderr, and the path to the output RDS file. + """ + # --- Input Validation --- + if not dcc_files_dir.is_dir(): + raise ValueError(f"DCC files directory not found: {dcc_files_dir}") + if not pkc_files: + raise ValueError("At least one PKC file must be provided.") + for pkc_file in pkc_files: + if not pkc_file.is_file(): + raise ValueError(f"PKC file not found: {pkc_file}") + if not pheno_data_file.is_file(): + raise ValueError(f"Phenotype data file not found: {pheno_data_file}") + + output_rds_path.parent.mkdir(parents=True, exist_ok=True) + + # --- R Script Generation --- + # Safely create R vectors from Python lists + r_protocol_cols = f'c({", ".join(f"{col}" for col in protocol_data_col_names)})' if protocol_data_col_names else "NULL" + r_experiment_cols = f'c({", ".join(f"{col}" for col in experiment_data_col_names)})' if experiment_data_col_names else "NULL" + r_pheno_sheet = f'"{pheno_data_sheet}"' if pheno_data_sheet else "NULL" + pkc_files_r_vector = f'c({", ".join(f"{str(p)}" for p in pkc_files)})' + + r_script_content = f""" + library(GeomxTools) + + tryCatch({{ + dcc_dir <- "{dcc_files_dir}" + pkc_files_vec <- {pkc_files_r_vector} + pheno_file <- "{pheno_data_file}" + output_path <- "{output_rds_path}" + pheno_dcc_col <- "{pheno_data_dcc_col_name}" + + dcc_files <- list.files(dcc_dir, pattern = "\\\\.dcc$", full.names = TRUE, recursive = TRUE) + if (length(dcc_files) == 0) {{ + stop("No .dcc files found in the specified directory.") + }} + + geomx_data <- readNanoStringGeoMxSet( + dccFiles = dcc_files, + pkcFiles = pkc_files_vec, + phenoDataFile = pheno_file, + phenoDataSheet = {r_pheno_sheet}, + phenoDataDccColName = pheno_dcc_col, + protocolDataColNames = {r_protocol_cols}, + experimentDataColNames = {r_experiment_cols} + ) + + saveRDS(geomx_data, file = output_path) + cat("Successfully created NanoStringGeomxSet object and saved to", output_path, "\\n") + }}, error = function(e) {{ + message("R script failed with error: ", e$message) + quit(status = 1) + }}) + """ + + # --- Subprocess Execution --- + command_executed = "" + try: + with tempfile.NamedTemporaryFile(mode='w', suffix=".R", delete=False, encoding='utf-8') as r_script_file: + r_script_file.write(r_script_content) + r_script_path = r_script_file.name + + cmd = ["Rscript", r_script_path] + command_executed = " ".join(cmd) + + process = subprocess.run(cmd, capture_output=True, text=True, check=True) + + return { + "command_executed": command_executed, + "stdout": process.stdout, + "stderr": process.stderr, + "output_files": [str(output_rds_path)] + } + except FileNotFoundError: + raise RuntimeError("Rscript not found. Please ensure R is installed and in your system's PATH.") + except subprocess.CalledProcessError as e: + return { + "command_executed": command_executed, + "stdout": e.stdout, + "stderr": e.stderr, + "error": "R script execution failed.", + "return_code": e.returncode, + "output_files": [] + } + finally: + if 'r_script_path' in locals() and Path(r_script_path).exists(): + Path(r_script_path).unlink() + +@mcp.tool() +def set_segment_qc_flags( + input_rds_path: Path, + output_rds_path: Path, + min_segment_reads: Optional[int] = 1000, + percent_aligned: Optional[float] = 80, + percent_saturation: Optional[float] = 50, + min_negative_count: Optional[int] = 10, + max_ntc_count: Optional[int] = 1000, + min_nuclei: Optional[int] = 200, + min_area: Optional[int] = 16000, +) -> dict: + """ + Sets segment QC flags in a NanoStringGeomxSet object based on specified cutoffs. + + Args: + input_rds_path: Path to the input RDS file containing a NanoStringGeomxSet object. + output_rds_path: Path for the output RDS file with QC flags applied. + min_segment_reads: Minimum number of reads in a segment. + percent_aligned: Minimum percentage of reads aligned. + percent_saturation: Minimum percentage of reads saturated. + min_negative_count: Minimum negative probe counts. + max_ntc_count: Maximum counts observed in NTC wells. + min_nuclei: Minimum number of nuclei in a segment. + min_area: Minimum area of a segment. + + Returns: + A dictionary containing the execution command, stdout, stderr, and the path to the output RDS file. + """ + # --- Input Validation --- + if not input_rds_path.is_file(): + raise ValueError(f"Input RDS file not found: {input_rds_path}") + output_rds_path.parent.mkdir(parents=True, exist_ok=True) + + # --- R Script Generation --- + r_script_content = f""" + library(GeomxTools) + + tryCatch({{ + input_rds <- "{input_rds_path}" + output_rds <- "{output_rds_path}" + + geomx_data <- readRDS(input_rds) + + qc_cutoffs <- list() + if (!is.null({min_segment_reads or 'NULL'})) {{ qc_cutoffs$minSegmentReads <- {min_segment_reads} }} + if (!is.null({percent_aligned or 'NULL'})) {{ qc_cutoffs$percentAligned <- {percent_aligned} }} + if (!is.null({percent_saturation or 'NULL'})) {{ qc_cutoffs$percentSaturation <- {percent_saturation} }} + if (!is.null({min_negative_count or 'NULL'})) {{ qc_cutoffs$minNegativeCount <- {min_negative_count} }} + if (!is.null({max_ntc_count or 'NULL'})) {{ qc_cutoffs$maxNTCCount <- {max_ntc_count} }} + if (!is.null({min_nuclei or 'NULL'})) {{ qc_cutoffs$minNuclei <- {min_nuclei} }} + if (!is.null({min_area or 'NULL'})) {{ qc_cutoffs$minArea <- {min_area} }} + + geomx_data_qc <- setSegmentQCFlags(geomx_data, qcCutoffs = qc_cutoffs) + + saveRDS(geomx_data_qc, file = output_rds) + cat("Successfully applied segment QC flags and saved to", output_rds, "\\n") + }}, error = function(e) {{ + message("R script failed with error: ", e$message) + quit(status = 1) + }}) + """ + + # --- Subprocess Execution --- + command_executed = "" + try: + with tempfile.NamedTemporaryFile(mode='w', suffix=".R", delete=False, encoding='utf-8') as r_script_file: + r_script_file.write(r_script_content) + r_script_path = r_script_file.name + + cmd = ["Rscript", r_script_path] + command_executed = " ".join(cmd) + + process = subprocess.run(cmd, capture_output=True, text=True, check=True) + + return { + "command_executed": command_executed, + "stdout": process.stdout, + "stderr": process.stderr, + "output_files": [str(output_rds_path)] + } + except FileNotFoundError: + raise RuntimeError("Rscript not found. Please ensure R is installed and in your system's PATH.") + except subprocess.CalledProcessError as e: + return { + "command_executed": command_executed, + "stdout": e.stdout, + "stderr": e.stderr, + "error": "R script execution failed.", + "return_code": e.returncode, + "output_files": [] + } + finally: + if 'r_script_path' in locals() and Path(r_script_path).exists(): + Path(r_script_path).unlink() + +@mcp.tool() +def set_bioprobe_qc_flags( + input_rds_path: Path, + output_rds_path: Path, + min_probe_ratio: float = 0.1, + percent_fail_grubbs: float = 20, + remove_local_outliers: bool = True, +) -> dict: + """ + Sets probe QC flags in a NanoStringGeomxSet object. + + Args: + input_rds_path: Path to the input RDS file containing a NanoStringGeomxSet object. + output_rds_path: Path for the output RDS file with QC flags applied. + min_probe_ratio: Minimum ratio of probes to the geometric mean of all probes. + percent_fail_grubbs: Percentage of segments that must fail Grubbs test for a probe to be flagged. + remove_local_outliers: If TRUE, local outliers will be removed. + + Returns: + A dictionary containing the execution command, stdout, stderr, and the path to the output RDS file. + """ + # --- Input Validation --- + if not input_rds_path.is_file(): + raise ValueError(f"Input RDS file not found: {input_rds_path}") + output_rds_path.parent.mkdir(parents=True, exist_ok=True) + + # --- R Script Generation --- + r_script_content = f""" + library(GeomxTools) + + tryCatch({{ + input_rds <- "{input_rds_path}" + output_rds <- "{output_rds_path}" + + geomx_data <- readRDS(input_rds) + + qc_cutoffs <- list( + minProbeRatio = {min_probe_ratio}, + percentFailGrubbs = {percent_fail_grubbs} + ) + + geomx_data_qc <- setBioProbeQCFlags( + geomx_data, + qcCutoffs = qc_cutoffs, + removeLocalOutliers = {str(remove_local_outliers).upper()} + ) + + saveRDS(geomx_data_qc, file = output_rds) + cat("Successfully applied bioprobe QC flags and saved to", output_rds, "\\n") + }}, error = function(e) {{ + message("R script failed with error: ", e$message) + quit(status = 1) + }}) + """ + + # --- Subprocess Execution --- + command_executed = "" + try: + with tempfile.NamedTemporaryFile(mode='w', suffix=".R", delete=False, encoding='utf-8') as r_script_file: + r_script_file.write(r_script_content) + r_script_path = r_script_file.name + + cmd = ["Rscript", r_script_path] + command_executed = " ".join(cmd) + + process = subprocess.run(cmd, capture_output=True, text=True, check=True) + + return { + "command_executed": command_executed, + "stdout": process.stdout, + "stderr": process.stderr, + "output_files": [str(output_rds_path)] + } + except FileNotFoundError: + raise RuntimeError("Rscript not found. Please ensure R is installed and in your system's PATH.") + except subprocess.CalledProcessError as e: + return { + "command_executed": command_executed, + "stdout": e.stdout, + "stderr": e.stderr, + "error": "R script execution failed.", + "return_code": e.returncode, + "output_files": [] + } + finally: + if 'r_script_path' in locals() and Path(r_script_path).exists(): + Path(r_script_path).unlink() + +@mcp.tool() +def subset_geomx_set( + input_rds_path: Path, + output_rds_path: Path, + subset_by_pdata: bool = True, + subset_column: Optional[str] = None, + subset_values: Optional[List[str]] = None, +) -> dict: + """ + Subsets a NanoStringGeomxSet object based on phenotype (pData) or feature (fData) annotations. + + Args: + input_rds_path: Path to the input RDS file containing a NanoStringGeomxSet object. + output_rds_path: Path for the output subsetted RDS file. + subset_by_pdata: If True, subset by sample annotations (pData). If False, subset by feature annotations (fData). + subset_column: The column name in pData or fData to use for subsetting. + subset_values: A list of values to keep from the subset_column. + + Returns: + A dictionary containing the execution command, stdout, stderr, and the path to the output RDS file. + """ + # --- Input Validation --- + if not input_rds_path.is_file(): + raise ValueError(f"Input RDS file not found: {input_rds_path}") + if not subset_column or not subset_values: + raise ValueError("subset_column and subset_values must be provided for subsetting.") + output_rds_path.parent.mkdir(parents=True, exist_ok=True) + + # --- R Script Generation --- + subset_values_r = f'c({", ".join(f"{v}" for v in subset_values)})' + + r_script_content = f""" + library(GeomxTools) + library(Biobase) + + tryCatch({{ + object <- readRDS("{input_rds_path}") + subset_col <- "{subset_column}" + subset_vals <- {subset_values_r} + + if ({str(subset_by_pdata).upper()}) {{ + logic <- pData(object)[[subset_col]] %in% subset_vals + logic[is.na(logic)] <- FALSE + subset_object <- object[, logic] + }} else {{ + logic <- fData(object)[[subset_col]] %in% subset_vals + logic[is.na(logic)] <- FALSE + subset_object <- object[logic, ] + }} + + saveRDS(subset_object, file = "{output_rds_path}") + cat("Successfully subset object and saved to", "{output_rds_path}", "\\n") + }}, error = function(e) {{ + message("R script failed with error: ", e$message) + quit(status = 1) + }}) + """ + + # --- Subprocess Execution --- + command_executed = "" + try: + with tempfile.NamedTemporaryFile(mode='w', suffix=".R", delete=False, encoding='utf-8') as r_script_file: + r_script_file.write(r_script_content) + r_script_path = r_script_file.name + + cmd = ["Rscript", r_script_path] + command_executed = " ".join(cmd) + + process = subprocess.run(cmd, capture_output=True, text=True, check=True) + + return { + "command_executed": command_executed, + "stdout": process.stdout, + "stderr": process.stderr, + "output_files": [str(output_rds_path)] + } + except FileNotFoundError: + raise RuntimeError("Rscript not found. Please ensure R is installed and in your system's PATH.") + except subprocess.CalledProcessError as e: + return { + "command_executed": command_executed, + "stdout": e.stdout, + "stderr": e.stderr, + "error": "R script execution failed.", + "return_code": e.returncode, + "output_files": [] + } + finally: + if 'r_script_path' in locals() and Path(r_script_path).exists(): + Path(r_script_path).unlink() + +@mcp.tool() +def aggregate_counts( + input_rds_path: Path, + output_rds_path: Path, + elt: str = "exprs", +) -> dict: + """ + Aggregates probe-level counts to the target level in a NanoStringGeomxSet object. + + Args: + input_rds_path: Path to the input RDS file containing a NanoStringGeomxSet object. + output_rds_path: Path for the output RDS file with aggregated counts. + elt: The name of the assay data element to aggregate. + + Returns: + A dictionary containing the execution command, stdout, stderr, and the path to the output RDS file. + """ + # --- Input Validation --- + if not input_rds_path.is_file(): + raise ValueError(f"Input RDS file not found: {input_rds_path}") + output_rds_path.parent.mkdir(parents=True, exist_ok=True) + + # --- R Script Generation --- + r_script_content = f""" + library(GeomxTools) + + tryCatch({{ + geomx_data <- readRDS("{input_rds_path}") + aggregated_data <- aggregateCounts(geomx_data, elt = "{elt}") + + saveRDS(aggregated_data, file = "{output_rds_path}") + cat("Successfully aggregated counts and saved to", "{output_rds_path}", "\\n") + }}, error = function(e) {{ + message("R script failed with error: ", e$message) + quit(status = 1) + }}) + """ + + # --- Subprocess Execution --- + command_executed = "" + try: + with tempfile.NamedTemporaryFile(mode='w', suffix=".R", delete=False, encoding='utf-8') as r_script_file: + r_script_file.write(r_script_content) + r_script_path = r_script_file.name + + cmd = ["Rscript", r_script_path] + command_executed = " ".join(cmd) + + process = subprocess.run(cmd, capture_output=True, text=True, check=True) + + return { + "command_executed": command_executed, + "stdout": process.stdout, + "stderr": process.stderr, + "output_files": [str(output_rds_path)] + } + except FileNotFoundError: + raise RuntimeError("Rscript not found. Please ensure R is installed and in your system's PATH.") + except subprocess.CalledProcessError as e: + return { + "command_executed": command_executed, + "stdout": e.stdout, + "stderr": e.stderr, + "error": "R script execution failed.", + "return_code": e.returncode, + "output_files": [] + } + finally: + if 'r_script_path' in locals() and Path(r_script_path).exists(): + Path(r_script_path).unlink() + +@mcp.tool() +def normalize_geomx( + input_rds_path: Path, + output_rds_path: Path, + norm_method: str, + from_elt: str = "exprs", + to_elt: str = "exprs_norm", + housekeepers: Optional[List[str]] = None, +) -> dict: + """ + Normalizes the count data in a NanoStringGeomxSet object. + + Args: + input_rds_path: Path to the input RDS file containing a NanoStringGeomxSet object. + output_rds_path: Path for the output RDS file with normalized data. + norm_method: Normalization method. Must be one of 'quant', 'neg', 'hk'. + from_elt: The assay data element to use for normalization. + to_elt: The name of the new assay data element to store normalized values. + housekeepers: A list of housekeeper gene names, required if norm_method is 'hk'. + + Returns: + A dictionary containing the execution command, stdout, stderr, and the path to the output RDS file. + """ + # --- Input Validation --- + if not input_rds_path.is_file(): + raise ValueError(f"Input RDS file not found: {input_rds_path}") + + valid_methods = ["quant", "neg", "hk"] + if norm_method not in valid_methods: + raise ValueError(f"Invalid norm_method '{norm_method}'. Must be one of {valid_methods}.") + + if norm_method == "hk" and not housekeepers: + raise ValueError("Housekeeper genes must be provided for 'hk' normalization.") + + output_rds_path.parent.mkdir(parents=True, exist_ok=True) + + housekeepers_r = f'c({", ".join(f"{hk}" for hk in housekeepers)})' if housekeepers else "NULL" + + # --- R Script Generation --- + r_script_content = f""" + library(GeomxTools) + + tryCatch({{ + geomx_data <- readRDS("{input_rds_path}") + + normalized_data <- normalize( + geomx_data, + norm.method = "{norm_method}", + fromElt = "{from_elt}", + toElt = "{to_elt}", + housekeepers = {housekeepers_r} + ) + + saveRDS(normalized_data, file = "{output_rds_path}") + cat("Successfully normalized data and saved to", "{output_rds_path}", "\\n") + }}, error = function(e) {{ + message("R script failed with error: ", e$message) + quit(status = 1) + }}) + """ + + # --- Subprocess Execution --- + command_executed = "" + try: + with tempfile.NamedTemporaryFile(mode='w', suffix=".R", delete=False, encoding='utf-8') as r_script_file: + r_script_file.write(r_script_content) + r_script_path = r_script_file.name + + cmd = ["Rscript", r_script_path] + command_executed = " ".join(cmd) + + process = subprocess.run(cmd, capture_output=True, text=True, check=True) + + return { + "command_executed": command_executed, + "stdout": process.stdout, + "stderr": process.stderr, + "output_files": [str(output_rds_path)] + } + except FileNotFoundError: + raise RuntimeError("Rscript not found. Please ensure R is installed and in your system's PATH.") + except subprocess.CalledProcessError as e: + return { + "command_executed": command_executed, + "stdout": e.stdout, + "stderr": e.stderr, + "error": "R script execution failed.", + "return_code": e.returncode, + "output_files": [] + } + finally: + if 'r_script_path' in locals() and Path(r_script_path).exists(): + Path(r_script_path).unlink() + +@mcp.tool() +def mixed_model_de( + input_rds_path: Path, + output_csv_path: Path, + elt: str, + model_formula_fixed: str, + group_var: str, + model_formula_random: Optional[str] = None, + contrasts: Optional[List[str]] = None, + n_cores: int = 1, +) -> dict: + """ + Performs differential expression analysis using a linear mixed model. + + Args: + input_rds_path: Path to the input RDS file containing a NanoStringGeomxSet object. + output_csv_path: Path for the output CSV file with DE results. + elt: The assay data element to use for the analysis (e.g., 'exprs_norm'). + model_formula_fixed: The fixed effects part of the model formula (e.g., 'region + diseaseStatus'). + group_var: The main variable of interest for testing (e.g., 'diseaseStatus'). + model_formula_random: The random effects part of the model formula (e.g., '(1|slideName)'). + contrasts: A list of contrasts to test (e.g., ['diseaseA - diseaseB', 'diseaseC - diseaseB']). + n_cores: Number of cores to use for parallel processing. + + Returns: + A dictionary containing the execution command, stdout, stderr, and the path to the output CSV file. + """ + # --- Input Validation --- + if not input_rds_path.is_file(): + raise ValueError(f"Input RDS file not found: {input_rds_path}") + if n_cores < 1: + raise ValueError("n_cores must be at least 1.") + output_csv_path.parent.mkdir(parents=True, exist_ok=True) + + # --- R Script Generation --- + formula_str = f"~ {model_formula_fixed}" + if model_formula_random: + formula_str += f" + {model_formula_random}" + + contrasts_r = "NULL" + if contrasts: + contrasts_r = f'c({", ".join(f"{c}" for c in contrasts)})' + + r_script_content = f""" + library(GeomxTools) + library(limma) + library(Biobase) + + tryCatch({{ + object <- readRDS("{input_rds_path}") + + model_formula <- as.formula("{formula_str}") + + contrast_matrix <- NULL + if (!is.null({contrasts_r})) {{ + contrast_matrix <- makeContrasts(contrasts = {contrasts_r}, levels = unique(pData(object)[["{group_var}"]])) + }} + + results <- mixedModelDE( + object, + elt = "{elt}", + modelFormula = model_formula, + groupVar = "{group_var}", + nCores = {n_cores}, + multiCore = {str(n_cores > 1).upper()}, + contrasts = contrast_matrix + ) + + write.csv(results, file = "{output_csv_path}", row.names = FALSE) + cat("Successfully performed DE analysis and saved results to", "{output_csv_path}", "\\n") + }}, error = function(e) {{ + message("R script failed with error: ", e$message) + quit(status = 1) + }}) + """ + + # --- Subprocess Execution --- + command_executed = "" + try: + with tempfile.NamedTemporaryFile(mode='w', suffix=".R", delete=False, encoding='utf-8') as r_script_file: + r_script_file.write(r_script_content) + r_script_path = r_script_file.name + + cmd = ["Rscript", r_script_path] + command_executed = " ".join(cmd) + + process = subprocess.run(cmd, capture_output=True, text=True, check=True) + + return { + "command_executed": command_executed, + "stdout": process.stdout, + "stderr": process.stderr, + "output_files": [str(output_csv_path)] + } + except FileNotFoundError: + raise RuntimeError("Rscript not found. Please ensure R is installed and in your system's PATH.") + except subprocess.CalledProcessError as e: + return { + "command_executed": command_executed, + "stdout": e.stdout, + "stderr": e.stderr, + "error": "R script execution failed.", + "return_code": e.returncode, + "output_files": [] + } + finally: + if 'r_script_path' in locals() and Path(r_script_path).exists(): + Path(r_script_path).unlink() \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-geomxtools/app/bioconductor-geomxtools_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-geomxtools/app/bioconductor-geomxtools_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..be40c6c3438cfec57312ff321d8f50319cb06bd8 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-geomxtools/app/bioconductor-geomxtools_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-geomxtools/app/bioconductor-geomxtools_server.py') +SERVER_NAME = 'biosci_bioconductor_geomxtools' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-geomxtools/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-geomxtools/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..ddcec77dc3ef15c0699109b8924d12c4ccb1737e --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-geomxtools/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-bioconductor-geomxtools: + build: . + image: mcp-bioconductor-geomxtools:latest + container_name: mcp-bioconductor-geomxtools + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=bioconductor-geomxtools + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-geomxtools/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-geomxtools/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..b21e8a4370c5f03f287e02d6c98afab673d9cd6b --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-geomxtools/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - bioconductor-geomxtools + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-geomxtools/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-geomxtools/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-geomxtools/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-ggspavis/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-ggspavis/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..cce68a8789108203349e557d81008954a56da088 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-ggspavis/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install bioconductor-ggspavis via conda (e.g., from bioconda) +RUN conda install -c bioconda bioconductor-ggspavis -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY app/bioconductor-ggspavis_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/bioconductor-ggspavis_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/bioconductor-ggspavis_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-ggspavis/app/bioconductor-ggspavis_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-ggspavis/app/bioconductor-ggspavis_server.py new file mode 100644 index 0000000000000000000000000000000000000000..33a6edfd4003ce2829eaf59e57d87b850ddc9f2d --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-ggspavis/app/bioconductor-ggspavis_server.py @@ -0,0 +1,395 @@ +import subprocess +import tempfile +import json +import shlex +from pathlib import Path +from typing import Optional, List, Literal + +# This R script acts as a generic command-line wrapper for ggspavis functions. +# It is written to a temporary file and executed by the Python functions below. +# It dynamically installs required R packages, making the tool self-contained. +R_SCRIPT = """ +# ggspavis_runner.R +# A generic runner for ggspavis functions + +# Suppress startup messages for cleaner output +suppressPackageStartupMessages({ + # Check if packages are installed, install if not + if (!requireNamespace("argparse", quietly = TRUE)) install.packages("argparse", repos = "http://cran.us.r-project.org") + if (!requireNamespace("jsonlite", quietly = TRUE)) install.packages("jsonlite", repos = "http://cran.us.r-project.org") + if (!requireNamespace("ggplot2", quietly = TRUE)) install.packages("ggplot2", repos = "http://cran.us.r-project.org") + if (!requireNamespace("BiocManager", quietly = TRUE)) install.packages("BiocManager", repos = "http://cran.us.r-project.org") + + # Use BiocManager to install Bioconductor packages + if (!requireNamespace("SpatialExperiment", quietly = TRUE)) BiocManager::install("SpatialExperiment", update = FALSE, ask = FALSE) + if (!requireNamespace("ggspavis", quietly = TRUE)) BiocManager::install("ggspavis", update = FALSE, ask = FALSE) +}) + +library(argparse) +library(jsonlite) +library(ggplot2) +library(SpatialExperiment) +library(ggspavis) + +# Main function to parse arguments and call the appropriate ggspavis function +main <- function() { + parser <- ArgumentParser(description = "A command-line wrapper for ggspavis functions.") + + parser$add_argument("func", help = "The ggspavis function to call (e.g., plotSpots, plotFeature).") + parser$add_argument("--spe_path", required = TRUE, help = "Path to the SpatialExperiment object (.rds file).") + parser$add_argument("--output_path", required = TRUE, help = "Path to save the output plot.") + parser$add_argument("--width", type = "double", default = 7, help = "Width of the output plot in inches.") + parser$add_argument("--height", type = "double", default = 7, help = "Height of the output plot in inches.") + parser$add_argument("--json_args", type = "character", default = "{}", help = "A JSON string of arguments for the ggspavis function.") + + args <- parser$parse_args() + + # Load SpatialExperiment object + if (!file.exists(args$spe_path)) { + stop("Input SPE file not found: ", args$spe_path) + } + spe <- readRDS(args$spe_path) + + # Parse JSON arguments for the target function + func_args <- fromJSON(args$json_args) + + # Add the main 'spe' object to the arguments list + func_args <- c(list(spe = spe), func_args) + + # Check if the requested function exists in the ggspavis package + if (!exists(args$func, where = "package:ggspavis", mode = "function")) { + stop("Function '", args$func, "' not found in ggspavis package.") + } + + # Dynamically call the ggspavis function with the parsed arguments + plot_obj <- do.call(args$func, func_args) + + # Save the generated plot to the specified output file + ggsave( + filename = args$output_path, + plot = plot_obj, + width = args$width, + height = args$height, + dpi = 300 + ) + + cat("Plot successfully saved to", args$output_path, "\\n") +} + +# Execute the main function and handle potential errors +tryCatch({ + main() +}, error = function(e) { + message("Error: ", e$message) + quit(status = 1) +}) +""" + +def _run_ggspavis( + function_name: str, + spe_path: Path, + output_path: Path, + plot_width: float, + plot_height: float, + json_args: dict, +) -> dict: + """ + Helper function to execute the ggspavis R script wrapper. + + Args: + function_name: The ggspavis function to execute (e.g., 'plotSpots'). + spe_path: Path to the input SpatialExperiment RDS file. + output_path: Path to save the output plot image. + plot_width: Width of the output plot in inches. + plot_height: Height of the output plot in inches. + json_args: A dictionary of arguments for the ggspavis function. + + Returns: + A dictionary containing the command executed, stdout, stderr, and output file path. + """ + # Ensure output directory exists + output_path.parent.mkdir(parents=True, exist_ok=True) + + # Use a temporary file to store and execute the R script + with tempfile.NamedTemporaryFile(mode='w', suffix=".R", delete=False) as r_script_file: + r_script_path = Path(r_script_file.name) + r_script_file.write(R_SCRIPT) + + command = [ + "Rscript", + str(r_script_path), + function_name, + "--spe_path", str(spe_path), + "--output_path", str(output_path), + "--width", str(plot_width), + "--height", str(plot_height), + "--json_args", json.dumps(json_args), + ] + + command_executed = " ".join(shlex.quote(c) for c in command) + + try: + process = subprocess.run( + command, + capture_output=True, + text=True, + check=True, + ) + stdout = process.stdout + stderr = process.stderr + output_files = {"plot": str(output_path)} if output_path.exists() else {} + + except FileNotFoundError: + # This error occurs if 'Rscript' is not in the system's PATH + return { + "command_executed": command_executed, + "stdout": "", + "stderr": "Error: 'Rscript' command not found. Please ensure R is installed and in your system's PATH.", + "output_files": {}, + } + except subprocess.CalledProcessError as e: + # This error occurs if the R script returns a non-zero exit code + return { + "command_executed": command_executed, + "stdout": e.stdout, + "stderr": e.stderr, + "output_files": {}, + } + finally: + # Clean up the temporary R script file + r_script_path.unlink() + + return { + "command_executed": command_executed, + "stdout": stdout, + "stderr": stderr, + "output_files": output_files, + } + + +@mcp.tool() +def ggspavis_plot_spots( + spe_path: Path, + output_filename: str, + fill: Optional[str] = None, + highlight: Optional[str] = None, + facet_by: Optional[str] = None, + assay: str = "logcounts", + palette: str = "viridis", + point_size: float = 1.5, + point_alpha: float = 1.0, + plot_width: float = 7.0, + plot_height: float = 7.0, +): + """ + Generates a spatial plot of spots/capture locations using ggspavis::plotSpots. + + Args: + spe_path: Path to the input SpatialExperiment object (.rds file). + output_filename: Name of the output plot file (e.g., 'plot.png'). + fill: Column name in colData or a feature name to use for spot fill color. + highlight: Column name in colData to use for highlighting spots. + facet_by: Column name in colData to use for faceting the plot. + assay: Name of the assay to use if 'fill' is a feature name. + palette: Color palette for continuous values (e.g., 'viridis', 'magma'). + point_size: Size of the points representing spots. + point_alpha: Alpha transparency of the points. + plot_width: Width of the output plot in inches. + plot_height: Height of the output plot in inches. + """ + # Input validation + if not spe_path.is_file(): + raise FileNotFoundError(f"Input file not found: {spe_path}") + if point_size <= 0: + raise ValueError("point_size must be positive.") + if not (0.0 <= point_alpha <= 1.0): + raise ValueError("point_alpha must be between 0.0 and 1.0.") + + # Prepare arguments for the R function + json_args = { + "fill": fill, + "highlight": highlight, + "facet_by": facet_by, + "assay": assay, + "palette": palette, + "point_size": point_size, + "point_alpha": point_alpha, + } + # Filter out None values so they are not passed in the JSON + json_args = {k: v for k, v in json_args.items() if v is not None} + + output_path = Path(output_filename) + + return _run_ggspavis( + function_name="plotSpots", + spe_path=spe_path, + output_path=output_path, + plot_width=plot_width, + plot_height=plot_height, + json_args=json_args, + ) + + +@mcp.tool() +def ggspavis_plot_feature( + spe_path: Path, + output_filename: str, + feature: str, + assay: str = "logcounts", + platform: Literal["Visium", "ST", "VisiumHD", "mIF", "seqFISH", "MERFISH", "Xenium"] = "Visium", + palette: str = "viridis", + point_size: float = 1.5, + plot_width: float = 7.0, + plot_height: float = 7.0, +): + """ + Visualizes the expression of a single feature (e.g., gene) in spatial context using ggspavis::plotFeature. + + Args: + spe_path: Path to the input SpatialExperiment object (.rds file). + output_filename: Name of the output plot file (e.g., 'feature_plot.png'). + feature: Name of the feature (gene) to plot from the specified assay. + assay: Name of the assay containing the feature. + platform: The spatial transcriptomics platform. + palette: Color palette for continuous expression values. + point_size: Size of the points. + plot_width: Width of the output plot in inches. + plot_height: Height of the output plot in inches. + """ + # Input validation + if not spe_path.is_file(): + raise FileNotFoundError(f"Input file not found: {spe_path}") + if not feature: + raise ValueError("A feature name must be provided.") + + # Prepare arguments for the R function + json_args = { + "feature": feature, + "assay": assay, + "platform": platform, + "palette": palette, + "point_size": point_size, + } + json_args = {k: v for k, v in json_args.items() if v is not None} + + output_path = Path(output_filename) + + return _run_ggspavis( + function_name="plotFeature", + spe_path=spe_path, + output_path=output_path, + plot_width=plot_width, + plot_height=plot_height, + json_args=json_args, + ) + + +@mcp.tool() +def ggspavis_plot_dim_red( + spe_path: Path, + output_filename: str, + reduction_type: str, + x_axis: Optional[str] = None, + y_axis: Optional[str] = None, + color_by: Optional[str] = None, + shape_by: Optional[str] = None, + assay: str = "logcounts", + point_size: float = 1.5, + plot_width: float = 7.0, + plot_height: float = 7.0, +): + """ + Plots dimensionality reduction results (e.g., UMAP, PCA) using ggspavis::plotDimRed. + + Args: + spe_path: Path to the input SpatialExperiment object (.rds file). + output_filename: Name of the output plot file (e.g., 'umap.png'). + reduction_type: Name of the dimensionality reduction result in reducedDims(spe). + x_axis: Column name for the x-axis (e.g., 'UMAP_1'). Defaults to type_1. + y_axis: Column name for the y-axis (e.g., 'UMAP_2'). Defaults to type_2. + color_by: Column name in colData or feature name to use for point color. + shape_by: Column name in colData to use for point shape. + assay: Name of assay to use if 'color_by' is a feature name. + point_size: Size of the points. + plot_width: Width of the output plot in inches. + plot_height: Height of the output plot in inches. + """ + # Input validation + if not spe_path.is_file(): + raise FileNotFoundError(f"Input file not found: {spe_path}") + if not reduction_type: + raise ValueError("A reduction_type must be provided.") + + # Prepare arguments for the R function + json_args = { + "type": reduction_type, + "x_axis": x_axis, + "y_axis": y_axis, + "color_by": color_by, + "shape_by": shape_by, + "assay": assay, + "point_size": point_size, + } + json_args = {k: v for k, v in json_args.items() if v is not None} + + output_path = Path(output_filename) + + return _run_ggspavis( + function_name="plotDimRed", + spe_path=spe_path, + output_path=output_path, + plot_width=plot_width, + plot_height=plot_height, + json_args=json_args, + ) + + +@mcp.tool() +def ggspavis_plot_label( + spe_path: Path, + output_filename: str, + label: str, + platform: Literal["Visium", "ST", "VisiumHD", "mIF", "seqFISH", "MERFISH", "Xenium"] = "Visium", + palette: Optional[str] = None, + point_size: float = 1.5, + plot_width: float = 7.0, + plot_height: float = 7.0, +): + """ + Visualizes spatial clusters or other categorical labels using ggspavis::plotLabel. + + Args: + spe_path: Path to the input SpatialExperiment object (.rds file). + output_filename: Name of the output plot file (e.g., 'clusters.png'). + label: Column name in colData containing the labels to plot. + platform: The spatial transcriptomics platform. + palette: Optional color palette for labels. If None, ggplot2 default is used. + point_size: Size of the points. + plot_width: Width of the output plot in inches. + plot_height: Height of the output plot in inches. + """ + # Input validation + if not spe_path.is_file(): + raise FileNotFoundError(f"Input file not found: {spe_path}") + if not label: + raise ValueError("A label column name must be provided.") + + # Prepare arguments for the R function + json_args = { + "label": label, + "platform": platform, + "palette": palette, + "point_size": point_size, + } + json_args = {k: v for k, v in json_args.items() if v is not None} + + output_path = Path(output_filename) + + return _run_ggspavis( + function_name="plotLabel", + spe_path=spe_path, + output_path=output_path, + plot_width=plot_width, + plot_height=plot_height, + json_args=json_args, + ) \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-ggspavis/app/bioconductor-ggspavis_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-ggspavis/app/bioconductor-ggspavis_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..a55e4ae47a938e459767fc9d013c33d84e0bb875 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-ggspavis/app/bioconductor-ggspavis_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-ggspavis/app/bioconductor-ggspavis_server.py') +SERVER_NAME = 'biosci_bioconductor_ggspavis' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-ggspavis/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-ggspavis/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-ggspavis/app/requirements.txt @@ -0,0 +1 @@ + diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-ggspavis/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-ggspavis/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..941920ed4e6d089e1a71dab40598b95cf6239402 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-ggspavis/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-bioconductor-ggspavis: + build: . + image: mcp-bioconductor-ggspavis:latest + container_name: mcp-bioconductor-ggspavis + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=bioconductor-ggspavis + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-ggspavis/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-ggspavis/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..282746043f724ae679e2f7e8ad158a7cb4b97be5 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-ggspavis/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - bioconductor-ggspavis + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-ggspavis/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-ggspavis/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-ggspavis/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-go.db/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-go.db/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-go.db/app/requirements.txt @@ -0,0 +1 @@ + diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-gosemsim/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-gosemsim/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..79759355a85d632d7922ea3c56930957dd4d59fa --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-gosemsim/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install bioconductor-gosemsim via conda (e.g., from bioconda) +RUN conda install -c bioconda bioconductor-gosemsim -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY app/bioconductor-gosemsim_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/bioconductor-gosemsim_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/bioconductor-gosemsim_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-gosemsim/app/bioconductor-gosemsim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-gosemsim/app/bioconductor-gosemsim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..a83d96584324e0d1d15417d34fe9cd5131891f2f --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-gosemsim/app/bioconductor-gosemsim_server.py @@ -0,0 +1,315 @@ +import subprocess +import tempfile +from pathlib import Path +from typing import List, Literal, Dict, Any + +# MCP decorator placeholder, as per the user's request to not import mcp +class mcp: + @staticmethod + def tool(): + def decorator(func): + return func + return decorator + +def _run_gosemsim_r_script(script_content: str, args: List[str]) -> Dict[str, Any]: + """ + A helper function to securely write and execute a temporary R script. + + This function handles the creation of a temporary R script file, executes it + with the provided arguments using `Rscript`, captures the output, and ensures + the temporary file is cleaned up afterward. It also includes robust error + + handling for cases where R is not installed or the script fails. + + Args: + script_content: A string containing the R code to be executed. + args: A list of string arguments to be passed to the R script. + + Returns: + A dictionary containing the execution details, including the command, + stdout, stderr, and return code. + """ + script_path = "" + try: + with tempfile.NamedTemporaryFile(mode='w', suffix=".R", delete=False) as r_script_file: + r_script_file.write(script_content) + script_path = r_script_file.name + + command = ["Rscript", script_path] + args + process = subprocess.run( + command, + capture_output=True, + text=True, + check=True, + encoding='utf-8' + ) + return { + "command_executed": " ".join(command), + "stdout": process.stdout.strip(), + "stderr": process.stderr.strip(), + "return_code": process.returncode, + } + except FileNotFoundError: + raise RuntimeError("Rscript not found. Please ensure R is installed and in your system's PATH.") + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(e.cmd), + "stdout": e.stdout.strip(), + "stderr": e.stderr.strip(), + "return_code": e.returncode, + "error": "R script execution failed. Check stderr for details from R." + } + finally: + if script_path and Path(script_path).exists(): + Path(script_path).unlink() + +@mcp.tool() +def gosemsim_gosim( + goid1: str, + goid2: str, + ont: Literal["BP", "MF", "CC"] = "MF", + measure: Literal["Wang", "Resnik", "Lin", "Rel", "Jiang"] = "Wang", + organism: Literal["human", "mouse", "rat", "yeast", "fly", "zebrafish"] = "human", +) -> Dict[str, Any]: + """ + Computes semantic similarity between two GO terms using GOSemSim::goSim. + + This tool requires R, the Bioconductor package GOSemSim, and the appropriate + organism annotation database (e.g., org.Hs.eg.db for human) to be installed. + + Args: + goid1: The first GO ID (e.g., "GO:0004022"). + goid2: The second GO ID (e.g., "GO:0004024"). + ont: The ontology to use. One of "BP", "MF", or "CC". + measure: The semantic similarity measure to use. + organism: The organism for which to compute the similarity. + + Returns: + A dictionary containing the execution result and the similarity score. + """ + if not goid1.startswith("GO:") or not goid2.startswith("GO:"): + raise ValueError("GO IDs must be in the format 'GO:#######'.") + + r_script_template = """ + args <- commandArgs(trailingOnly=TRUE) + if (length(args) != 5) stop("Internal error: Expected 5 arguments.", call.=FALSE) + goid1 <- args[1]; goid2 <- args[2]; ont <- args[3]; measure <- args[4]; organism <- args[5] + + suppressPackageStartupMessages(library(GOSemSim)) + org_db_map <- c("human"="org.Hs.eg.db", "mouse"="org.Mm.eg.db", "rat"="org.Rn.eg.db", "yeast"="org.Sc.sgd.db", "fly"="org.Dm.eg.db", "zebrafish"="org.Dr.eg.db") + org_db_pkg <- org_db_map[organism] + + if (!requireNamespace(org_db_pkg, quietly = TRUE)) stop(paste("Package", org_db_pkg, "is not installed."), call. = FALSE) + suppressPackageStartupMessages(library(org_db_pkg, character.only = TRUE)) + + sem_data <- godata(OrgDb = get(org_db_pkg), ont = ont, keytype = "ENTREZID") + result <- tryCatch(goSim(goid1, goid2, semData=sem_data, measure=measure), error = function(e) { message(e$message); NA }) + if (!is.na(result)) cat(result) else quit(status = 1) + """ + + script_args = [goid1, goid2, ont, measure, organism] + result = _run_gosemsim_r_script(r_script_template, script_args) + + if result.get("return_code") == 0 and result.get("stdout"): + try: + result["similarity_score"] = float(result["stdout"]) + except (ValueError, TypeError): + result["error"] = "Failed to parse similarity score from R script output." + + return result + +@mcp.tool() +def gosemsim_mgosim( + go_ids1: List[str], + go_ids2: List[str], + output_csv: Path, + ont: Literal["BP", "MF", "CC"] = "MF", + measure: Literal["Wang", "Resnik", "Lin", "Rel", "Jiang"] = "Wang", + combine: Literal["max", "avg", "rcmax", "BMA"] = "BMA", + organism: Literal["human", "mouse", "rat", "yeast", "fly", "zebrafish"] = "human", +) -> Dict[str, Any]: + """ + Computes semantic similarity between two sets of GO terms using GOSemSim::mgoSim. + + This tool requires R, GOSemSim, and the appropriate organism annotation database. + The resulting similarity matrix is saved to the specified output CSV file. + + Args: + go_ids1: A list of GO IDs for the first set. + go_ids2: A list of GO IDs for the second set. + output_csv: Path to save the resulting similarity matrix in CSV format. + ont: The ontology to use ("BP", "MF", or "CC"). + measure: The semantic similarity measure to use. + combine: The method to combine scores for GO term sets. + organism: The organism for which to compute the similarity. + + Returns: + A dictionary containing the execution result and the path to the output file. + """ + if not go_ids1 or not go_ids2: + raise ValueError("Input GO ID lists cannot be empty.") + for go_id in go_ids1 + go_ids2: + if not go_id.startswith("GO:"): + raise ValueError(f"Invalid GO ID format: {go_id}. Must be 'GO:#######'.") + + go_ids1_str = ",".join(go_ids1) + go_ids2_str = ",".join(go_ids2) + + r_script_template = """ + args <- commandArgs(trailingOnly=TRUE) + if (length(args) != 6) stop("Internal error: Expected 6 arguments.", call.=FALSE) + go_ids1 <- strsplit(args[1], ",")[[1]]; go_ids2 <- strsplit(args[2], ",")[[1]]; ont <- args[3]; measure <- args[4]; combine <- args[5]; organism <- args[6] + + suppressPackageStartupMessages(library(GOSemSim)) + org_db_map <- c("human"="org.Hs.eg.db", "mouse"="org.Mm.eg.db", "rat"="org.Rn.eg.db", "yeast"="org.Sc.sgd.db", "fly"="org.Dm.eg.db", "zebrafish"="org.Dr.eg.db") + org_db_pkg <- org_db_map[organism] + + if (!requireNamespace(org_db_pkg, quietly = TRUE)) stop(paste("Package", org_db_pkg, "is not installed."), call. = FALSE) + suppressPackageStartupMessages(library(org_db_pkg, character.only = TRUE)) + + sem_data <- godata(OrgDb = get(org_db_pkg), ont = ont, keytype = "ENTREZID") + result_matrix <- tryCatch(mgoSim(go_ids1, go_ids2, semData=sem_data, measure=measure, combine=combine), error = function(e) { message(e$message); NULL }) + if (!is.null(result_matrix)) write.csv(result_matrix, stdout(), row.names=TRUE, quote=FALSE) else quit(status = 1) + """ + + script_args = [go_ids1_str, go_ids2_str, ont, measure, combine, organism] + result = _run_gosemsim_r_script(r_script_template, script_args) + + output_files = [] + if result.get("return_code") == 0 and result.get("stdout"): + try: + output_csv.parent.mkdir(parents=True, exist_ok=True) + output_csv.write_text(result["stdout"]) + output_files.append(str(output_csv)) + except IOError as e: + result["error"] = f"Failed to write output file: {e}" + + result["output_files"] = output_files + return result + +@mcp.tool() +def gosemsim_genesim( + gene1: str, + gene2: str, + ont: Literal["BP", "MF", "CC"] = "MF", + measure: Literal["Wang", "Resnik", "Lin", "Rel", "Jiang"] = "Wang", + organism: Literal["human", "mouse", "rat", "yeast", "fly", "zebrafish"] = "human", + combine: Literal["max", "avg", "rcmax", "BMA"] = "BMA", +) -> Dict[str, Any]: + """ + Computes semantic similarity between two genes using GOSemSim::geneSim. + + This tool requires R, GOSemSim, and the appropriate organism annotation database. + Gene IDs should be Entrez Gene IDs. + + Args: + gene1: The first Entrez Gene ID. + gene2: The second Entrez Gene ID. + ont: The ontology to use ("BP", "MF", or "CC"). + measure: The semantic similarity measure to use. + organism: The organism for which to compute the similarity. + combine: The method to combine scores for the gene's GO term sets. + + Returns: + A dictionary containing the execution result and the similarity score. + """ + if not gene1.isdigit() or not gene2.isdigit(): + raise ValueError("Gene IDs must be numeric Entrez IDs.") + + r_script_template = """ + args <- commandArgs(trailingOnly=TRUE) + if (length(args) != 6) stop("Internal error: Expected 6 arguments.", call.=FALSE) + gene1 <- args[1]; gene2 <- args[2]; ont <- args[3]; measure <- args[4]; organism <- args[5]; combine <- args[6] + + suppressPackageStartupMessages(library(GOSemSim)) + org_db_map <- c("human"="org.Hs.eg.db", "mouse"="org.Mm.eg.db", "rat"="org.Rn.eg.db", "yeast"="org.Sc.sgd.db", "fly"="org.Dm.eg.db", "zebrafish"="org.Dr.eg.db") + org_db_pkg <- org_db_map[organism] + + if (!requireNamespace(org_db_pkg, quietly = TRUE)) stop(paste("Package", org_db_pkg, "is not installed."), call. = FALSE) + suppressPackageStartupMessages(library(org_db_pkg, character.only = TRUE)) + + sem_data <- godata(OrgDb = get(org_db_pkg), ont = ont, keytype = "ENTREZID") + result <- tryCatch(geneSim(gene1, gene2, semData=sem_data, measure=measure, combine=combine), error = function(e) { message(e$message); NA }) + if (!is.na(result$geneSim)) cat(result$geneSim) else quit(status = 1) + """ + + script_args = [gene1, gene2, ont, measure, organism, combine] + result = _run_gosemsim_r_script(r_script_template, script_args) + + if result.get("return_code") == 0 and result.get("stdout"): + try: + result["similarity_score"] = float(result["stdout"]) + except (ValueError, TypeError): + result["error"] = "Failed to parse similarity score from R script output." + + return result + +@mcp.tool() +def gosemsim_mgenesim( + genes1: List[str], + genes2: List[str], + output_csv: Path, + ont: Literal["BP", "MF", "CC"] = "MF", + measure: Literal["Wang", "Resnik", "Lin", "Rel", "Jiang"] = "Wang", + organism: Literal["human", "mouse", "rat", "yeast", "fly", "zebrafish"] = "human", + combine: Literal["max", "avg", "rcmax", "BMA"] = "BMA", +) -> Dict[str, Any]: + """ + Computes semantic similarity between two gene clusters using GOSemSim::mgeneSim. + + This tool requires R, GOSemSim, and the appropriate organism annotation database. + Gene IDs should be Entrez Gene IDs. The resulting similarity matrix is saved + to the specified output CSV file. + + Args: + genes1: A list of Entrez Gene IDs for the first cluster. + genes2: A list of Entrez Gene IDs for the second cluster. + output_csv: Path to save the resulting similarity matrix in CSV format. + ont: The ontology to use ("BP", "MF", or "CC"). + measure: The semantic similarity measure to use. + organism: The organism for which to compute the similarity. + combine: The method to combine scores for GO term sets. + + Returns: + A dictionary containing the execution result and the path to the output file. + """ + if not genes1 or not genes2: + raise ValueError("Input gene lists cannot be empty.") + for gene_id in genes1 + genes2: + if not gene_id.isdigit(): + raise ValueError(f"Invalid Gene ID format: {gene_id}. Must be a numeric Entrez ID.") + + genes1_str = ",".join(genes1) + genes2_str = ",".join(genes2) + + r_script_template = """ + args <- commandArgs(trailingOnly=TRUE) + if (length(args) != 6) stop("Internal error: Expected 6 arguments.", call.=FALSE) + genes1 <- strsplit(args[1], ",")[[1]]; genes2 <- strsplit(args[2], ",")[[1]]; ont <- args[3]; measure <- args[4]; organism <- args[5]; combine <- args[6] + + suppressPackageStartupMessages(library(GOSemSim)) + org_db_map <- c("human"="org.Hs.eg.db", "mouse"="org.Mm.eg.db", "rat"="org.Rn.eg.db", "yeast"="org.Sc.sgd.db", "fly"="org.Dm.eg.db", "zebrafish"="org.Dr.eg.db") + org_db_pkg <- org_db_map[organism] + + if (!requireNamespace(org_db_pkg, quietly = TRUE)) stop(paste("Package", org_db_pkg, "is not installed."), call. = FALSE) + suppressPackageStartupMessages(library(org_db_pkg, character.only = TRUE)) + + sem_data <- godata(OrgDb = get(org_db_pkg), ont = ont, keytype = "ENTREZID") + result_matrix <- tryCatch(mgeneSim(genes1, genes2, semData=sem_data, measure=measure, combine=combine), error = function(e) { message(e$message); NULL }) + if (!is.null(result_matrix)) write.csv(result_matrix, stdout(), row.names=TRUE, quote=FALSE) else quit(status = 1) + """ + + script_args = [genes1_str, genes2_str, ont, measure, organism, combine] + result = _run_gosemsim_r_script(r_script_template, script_args) + + output_files = [] + if result.get("return_code") == 0 and result.get("stdout"): + try: + output_csv.parent.mkdir(parents=True, exist_ok=True) + output_csv.write_text(result["stdout"]) + output_files.append(str(output_csv)) + except IOError as e: + result["error"] = f"Failed to write output file: {e}" + + result["output_files"] = output_files + return result \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-gosemsim/app/bioconductor-gosemsim_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-gosemsim/app/bioconductor-gosemsim_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..1093791134811e3222ae97c9961fe4e090c9616b --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-gosemsim/app/bioconductor-gosemsim_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-gosemsim/app/bioconductor-gosemsim_server.py') +SERVER_NAME = 'biosci_bioconductor_gosemsim' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-gosemsim/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-gosemsim/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-gosemsim/app/requirements.txt @@ -0,0 +1 @@ + diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-gosemsim/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-gosemsim/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..e0264312aed56c58603128d118ddb6f05d42616d --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-gosemsim/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-bioconductor-gosemsim: + build: . + image: mcp-bioconductor-gosemsim:latest + container_name: mcp-bioconductor-gosemsim + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=bioconductor-gosemsim + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-gosemsim/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-gosemsim/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..8f13aa43e05a5c4edac218b82b7b0981931e9030 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-gosemsim/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - bioconductor-gosemsim + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-gosemsim/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-gosemsim/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-gosemsim/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-hcatonsildata/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-hcatonsildata/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..a2e06873042488f4adf3a3d6e09eb83fc0f670cc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-hcatonsildata/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install bioconductor-hcatonsildata via conda (e.g., from bioconda) +RUN conda install -c bioconda bioconductor-hcatonsildata -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY app/bioconductor-hcatonsildata_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/bioconductor-hcatonsildata_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/bioconductor-hcatonsildata_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-hcatonsildata/app/bioconductor-hcatonsildata_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-hcatonsildata/app/bioconductor-hcatonsildata_server.py new file mode 100644 index 0000000000000000000000000000000000000000..ce33bf01581e3e6c0e78a678a98fb41e2d2097da --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-hcatonsildata/app/bioconductor-hcatonsildata_server.py @@ -0,0 +1,155 @@ +import subprocess +import tempfile +import shutil +from pathlib import Path +from typing import Optional, List, Dict, Any + +# Mock decorator for standalone execution +class mcp: + def tool(func): + return func + +@mcp.tool +def hcatonsildata_load( + output_file: Path, + version: str = "3.1.3", + ensembl: bool = False, + location: bool = True, + output_format: str = "h5ad", +) -> Dict[str, Any]: + """ + Loads the HCATonsilData object from Bioconductor and saves it to a specified file format. + + This tool acts as a wrapper around the R/Bioconductor HCATonsilData package, + which provides access to a SingleCellExperiment object of human tonsil data from the + Human Cell Atlas project. Since the package itself is not a command-line tool, this + function generates and executes an R script to load the data and export it. + + Args: + output_file: The path to the output file where the data object will be saved. + version: The version of the dataset to load. Must be either "3.1.3" or "2.0.0". + ensembl: A boolean indicating whether to use Ensembl identifiers for gene annotation. + If False, gene symbols are used. + location: A boolean indicating whether to download the data to a default + ExperimentHub cache location. + output_format: The format for the output file. Must be either "h5ad" (for AnnData) + or "rds" (for an R Data Serialization file). + + Returns: + A dictionary containing the execution details, including the command, + stdout, stderr, and the path to the output file. + """ + # 1. Input Validation + allowed_versions = ["3.1.3", "2.0.0"] + if version not in allowed_versions: + raise ValueError(f"Invalid version '{version}'. Must be one of {allowed_versions}.") + + allowed_formats = ["h5ad", "rds"] + if output_format not in allowed_formats: + raise ValueError(f"Invalid output_format '{output_format}'. Must be one of {allowed_formats}.") + + if not shutil.which("Rscript"): + raise FileNotFoundError("Rscript executable not found in PATH. R is required to run this tool.") + + # 2. File Path Handling + output_file = Path(output_file).resolve() + output_file.parent.mkdir(parents=True, exist_ok=True) + + # Convert Python bools to R's TRUE/FALSE strings + ensembl_r = str(ensembl).upper() + location_r = str(location).upper() + + # 3. R Script Generation + r_script_content = f""" + # Ensure all required packages are available + if (!requireNamespace("BiocManager", quietly = TRUE)) {{ + install.packages("BiocManager", repos = "http://cran.us.r-project.org") + }} + if (!requireNamespace("HCATonsilData", quietly = TRUE)) {{ + BiocManager::install("HCATonsilData", update=FALSE, ask=FALSE) + }} + + if ("{output_format}" == "h5ad") {{ + if (!requireNamespace("zellkonverter", quietly = TRUE)) {{ + BiocManager::install("zellkonverter", update=FALSE, ask=FALSE) + }} + library(zellkonverter) + }} + + library(HCATonsilData) + library(SingleCellExperiment) + + cat("Loading HCATonsilData version {version}...\\n") + + # Load the data object + sce <- HCATonsilData( + version = "{version}", + ensembl = {ensembl_r}, + location = {location_r} + ) + + cat("Saving data object to {str(output_file)} in {output_format} format...\\n") + + # Save the object in the specified format + if ("{output_format}" == "h5ad") {{ + writeH5AD(sce, file = "{str(output_file)}", overwrite = TRUE) + }} else if ("{output_format}" == "rds") {{ + saveRDS(sce, file = "{str(output_file)}") + }} + + cat("Process complete.\\n") + """ + + # 4. Subprocess Execution + command = [] + stdout_str = "" + stderr_str = "" + + try: + with tempfile.NamedTemporaryFile(mode='w', suffix=".R", delete=False) as tmp_r_script: + tmp_r_script.write(r_script_content) + script_path = tmp_r_script.name + + command = ["Rscript", script_path] + + process = subprocess.run( + command, + capture_output=True, + text=True, + check=True, + encoding='utf-8' + ) + stdout_str = process.stdout + stderr_str = process.stderr + + except subprocess.CalledProcessError as e: + # 5. Error Handling + return { + "command_executed": " ".join(command), + "stdout": e.stdout, + "stderr": e.stderr, + "return_code": e.returncode, + "error": "R script execution failed.", + "output_files": [] + } + except FileNotFoundError: + return { + "command_executed": " ".join(command), + "stdout": "", + "stderr": "Rscript executable not found. Please ensure R is installed and in your system's PATH.", + "return_code": 1, + "error": "Dependency not found.", + "output_files": [] + } + finally: + # Clean up the temporary R script + if 'script_path' in locals() and Path(script_path).exists(): + Path(script_path).unlink() + + # 6. Structured Result Return + return { + "command_executed": " ".join(command), + "stdout": stdout_str, + "stderr": stderr_str, + "output_files": [str(output_file)] + } \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-hcatonsildata/app/bioconductor-hcatonsildata_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-hcatonsildata/app/bioconductor-hcatonsildata_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..d1f74df5b3bef023f855ae8aa53356bcd7cfcfae --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-hcatonsildata/app/bioconductor-hcatonsildata_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-hcatonsildata/app/bioconductor-hcatonsildata_server.py') +SERVER_NAME = 'biosci_bioconductor_hcatonsildata' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-hcatonsildata/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-hcatonsildata/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-hcatonsildata/app/requirements.txt @@ -0,0 +1 @@ + diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-hcatonsildata/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-hcatonsildata/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..26b78c36f0c42e04db4de93abfa037953221a302 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-hcatonsildata/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-bioconductor-hcatonsildata: + build: . + image: mcp-bioconductor-hcatonsildata:latest + container_name: mcp-bioconductor-hcatonsildata + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=bioconductor-hcatonsildata + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-hcatonsildata/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-hcatonsildata/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..0fb5f539e7d8c659bb01c6cc21fdc219a3330885 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-hcatonsildata/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - bioconductor-hcatonsildata + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-hcatonsildata/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-hcatonsildata/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-hcatonsildata/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-hoodscanr/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-hoodscanr/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-hoodscanr/app/requirements.txt @@ -0,0 +1 @@ + diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-lisaclust/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-lisaclust/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..533d0c71a06e3b81965bf05fa7ecdab344877e1f --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-lisaclust/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install bioconductor-lisaclust via conda (e.g., from bioconda) +RUN conda install -c bioconda bioconductor-lisaclust -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY app/bioconductor-lisaclust_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/bioconductor-lisaclust_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/bioconductor-lisaclust_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-lisaclust/app/bioconductor-lisaclust_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-lisaclust/app/bioconductor-lisaclust_server.py new file mode 100644 index 0000000000000000000000000000000000000000..a5a1626e37d6dd8274158c67fba684e6bfd957ff --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-lisaclust/app/bioconductor-lisaclust_server.py @@ -0,0 +1,467 @@ +import subprocess +import tempfile +from pathlib import Path +from typing import List, Optional + +@mcp.tool() +def lisa_calculate( + input_rds: Path, + output_rds: Path, + cell_type_col: str, + image_id_col: str, + spatial_coords: List[str] = ["x", "y"], + rs: List[float] = [50.0, 100.0], +) -> dict: + """ + Calculate Local Indicators of Spatial Association (LISA) for cell-type colocalization. + + This function wraps the `lisa()` function from the R package `lisaClust`. + It requires R and the Bioconductor packages: lisaClust, SingleCellExperiment, SpatialExperiment. + + Args: + input_rds: Path to an RDS file containing a SingleCellExperiment or SpatialExperiment object. + output_rds: Path where the updated RDS file with LISA values will be saved in the metadata. + cell_type_col: Column name in colData containing cell type labels. + image_id_col: Column name in colData containing image/sample identifiers. + spatial_coords: List of two column names for spatial coordinates. + rs: List of radii at which to calculate LISA. + """ + # Input validation + if not input_rds.exists(): + raise FileNotFoundError(f"Input file not found: {input_rds}") + if len(spatial_coords) != 2: + raise ValueError("spatial_coords must contain exactly two column names.") + if not rs: + raise ValueError("rs list cannot be empty.") + if not all(r > 0 for r in rs): + raise ValueError("All radii in rs must be positive.") + + # Ensure output directory exists + output_rds.parent.mkdir(parents=True, exist_ok=True) + + # Convert Python lists to R vector strings + rs_r = f"c({', '.join(map(str, rs))})" + quoted_coords = [f'"{c}"' for c in spatial_coords] + coords_r = f"c({', '.join(quoted_coords)})" + + # Use .as_posix() for cross-platform path compatibility in the R script + input_path_posix = input_rds.as_posix() + output_path_posix = output_rds.as_posix() + + r_script = f""" + library(lisaClust) + library(SingleCellExperiment) + library(SpatialExperiment) + + cells <- readRDS("{input_path_posix}") + lisa_values <- lisa( + cells, + cellType = "{cell_type_col}", + imageID = "{image_id_col}", + spatialCoords = {coords_r}, + Rs = {rs_r} + ) + + # Store LISA values in the object metadata for downstream clustering. + metadata(cells)$lisa <- lisa_values + saveRDS(cells, "{output_path_posix}") + """ + + tmp_path = None + cmd = [] + try: + with tempfile.NamedTemporaryFile(mode="w", suffix=".R", delete=False, encoding="utf-8") as tmp: + tmp.write(r_script) + tmp_path = tmp.name + + cmd = ["Rscript", tmp_path] + result = subprocess.run(cmd, capture_output=True, text=True, check=True, encoding="utf-8") + + return { + "command_executed": " ".join(cmd), + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": [str(output_rds)] + } + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(cmd), + "stdout": e.stdout, + "stderr": e.stderr, + "error": "R script execution failed." + } + finally: + if tmp_path and Path(tmp_path).exists(): + Path(tmp_path).unlink() + +@mcp.tool() +def lisa_cluster( + input_rds: Path, + output_rds: Path, + k: int, + cell_type_col: str, + image_id_col: str, + spatial_coords: List[str] = ["x", "y"], + rs: List[float] = [50.0, 100.0], + region_name: str = "region" +) -> dict: + """ + Cluster cells based on LISA values to identify tissue regions. + + This function wraps `lisaClust()`. It can use pre-calculated LISA values stored + in the input object's metadata or calculate them on the fly. + + Args: + input_rds: Path to an RDS file containing a SingleCellExperiment or SpatialExperiment object. + output_rds: Path where the updated RDS file with cluster assignments will be saved. + k: Number of clusters (regions) to identify. + cell_type_col: Column name for cell type labels (used if LISA values are not pre-calculated). + image_id_col: Column name for image IDs (used if LISA values are not pre-calculated). + spatial_coords: Column names for spatial coordinates (used if LISA values are not pre-calculated). + rs: Radii for LISA calculation (used if LISA values are not pre-calculated). + region_name: Name of the column to store cluster assignments in colData. + """ + # Input validation + if not input_rds.exists(): + raise FileNotFoundError(f"Input file not found: {input_rds}") + if k <= 0: + raise ValueError("k (number of clusters) must be a positive integer.") + if len(spatial_coords) != 2: + raise ValueError("spatial_coords must contain exactly two column names.") + if not rs: + raise ValueError("rs list cannot be empty.") + + # Ensure output directory exists + output_rds.parent.mkdir(parents=True, exist_ok=True) + + # Convert Python lists to R vector strings + rs_r = f"c({', '.join(map(str, rs))})" + quoted_coords = [f'"{c}"' for c in spatial_coords] + coords_r = f"c({', '.join(quoted_coords)})" + + # Use .as_posix() for cross-platform path compatibility + input_path_posix = input_rds.as_posix() + output_path_posix = output_rds.as_posix() + + r_script = f""" + library(lisaClust) + library(SingleCellExperiment) + library(SpatialExperiment) + + cells <- readRDS("{input_path_posix}") + + # lisaClust can use pre-calculated LISA values from metadata or calculate them. + # We pass all parameters; if metadata(cells)$lisa exists, it will be used, + # otherwise, lisa values will be calculated using the other provided parameters. + lisa_pre <- NULL + if ("lisa" %in% names(metadata(cells))) {{ + lisa_pre <- metadata(cells)$lisa + }} + + cells <- lisaClust( + cells, + k = {k}, + lisa = lisa_pre, + cellType = "{cell_type_col}", + imageID = "{image_id_col}", + spatialCoords = {coords_r}, + Rs = {rs_r} + ) + + # Rename the default 'region' column if a different name is requested + if ("{region_name}" != "region") {{ + colData(cells)[["{region_name}"]] <- colData(cells)$region + colData(cells)$region <- NULL + }} + + saveRDS(cells, "{output_path_posix}") + """ + + tmp_path = None + cmd = [] + try: + with tempfile.NamedTemporaryFile(mode="w", suffix=".R", delete=False, encoding="utf-8") as tmp: + tmp.write(r_script) + tmp_path = tmp.name + + cmd = ["Rscript", tmp_path] + result = subprocess.run(cmd, capture_output=True, text=True, check=True, encoding="utf-8") + + return { + "command_executed": " ".join(cmd), + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": [str(output_rds)] + } + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(cmd), + "stdout": e.stdout, + "stderr": e.stderr, + "error": "R script execution failed." + } + finally: + if tmp_path and Path(tmp_path).exists(): + Path(tmp_path).unlink() + +@mcp.tool() +def lisa_hatch_plot( + input_rds: Path, + output_plot: Path, + region_col: str = "region", + cell_type_col: str = "cellType", + image_id: Optional[str] = None, + width: int = 10, + height: int = 8 +) -> dict: + """ + Generate a hatch plot to visualize cell-type colocalization within identified regions. + + This function wraps the `hatchPlot()` function from `lisaClust`. + + Args: + input_rds: Path to an RDS file with lisaClust results. + output_plot: Path to save the output plot (e.g., plot.png, plot.pdf). + region_col: Column name in colData containing region clusters. + cell_type_col: Column name in colData containing cell type labels. + image_id: Specific ImageID to plot. If None, the first available image is used. + width: Width of the output plot in inches. + height: Height of the output plot in inches. + """ + # Input validation + if not input_rds.exists(): + raise FileNotFoundError(f"Input file not found: {input_rds}") + if width <= 0 or height <= 0: + raise ValueError("Plot width and height must be positive.") + + # Ensure output directory exists + output_plot.parent.mkdir(parents=True, exist_ok=True) + + # Use .as_posix() for cross-platform path compatibility + input_path_posix = input_rds.as_posix() + output_path_posix = output_plot.as_posix() + + # R logic to select an image_id if not provided + img_id_r = f'"{image_id}"' if image_id else "unique(colData(cells)$imageID)[1]" + + r_script = f""" + library(lisaClust) + library(ggplot2) + library(SingleCellExperiment) + + cells <- readRDS("{input_path_posix}") + + # Subset to a single image for plotting + image_to_plot <- {img_id_r} + cells_sub <- cells[, colData(cells)$imageID == image_to_plot] + + p <- hatchPlot( + cells_sub, + region = "{region_col}", + cellType = "{cell_type_col}" + ) + + ggsave("{output_path_posix}", plot = p, width = {width}, height = {height}) + """ + + tmp_path = None + cmd = [] + try: + with tempfile.NamedTemporaryFile(mode="w", suffix=".R", delete=False, encoding="utf-8") as tmp: + tmp.write(r_script) + tmp_path = tmp.name + + cmd = ["Rscript", tmp_path] + result = subprocess.run(cmd, capture_output=True, text=True, check=True, encoding="utf-8") + + return { + "command_executed": " ".join(cmd), + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": [str(output_plot)] + } + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(cmd), + "stdout": e.stdout, + "stderr": e.stderr, + "error": "R script execution failed." + } + finally: + if tmp_path and Path(tmp_path).exists(): + Path(tmp_path).unlink() + +@mcp.tool() +def lisa_region_map( + input_rds: Path, + output_plot: Path, + region_col: str = "region", + image_id: Optional[str] = None, + width: int = 10, + height: int = 8 +) -> dict: + """ + Generate a region map to visualize the spatial distribution of identified regions. + + This function wraps the `regionMap()` function from `lisaClust`. + + Args: + input_rds: Path to an RDS file with lisaClust results. + output_plot: Path to save the output plot (e.g., plot.png, plot.pdf). + region_col: Column name in colData containing region clusters. + image_id: Specific ImageID to plot. If None, the first available image is used. + width: Width of the output plot in inches. + height: Height of the output plot in inches. + """ + # Input validation + if not input_rds.exists(): + raise FileNotFoundError(f"Input file not found: {input_rds}") + if width <= 0 or height <= 0: + raise ValueError("Plot width and height must be positive.") + + # Ensure output directory exists + output_plot.parent.mkdir(parents=True, exist_ok=True) + + # Use .as_posix() for cross-platform path compatibility + input_path_posix = input_rds.as_posix() + output_path_posix = output_plot.as_posix() + + # R logic to select an image_id if not provided + img_id_r = f'"{image_id}"' if image_id else "unique(colData(cells)$imageID)[1]" + + r_script = f""" + library(lisaClust) + library(ggplot2) + library(SingleCellExperiment) + + cells <- readRDS("{input_path_posix}") + + # Subset to a single image for plotting + image_to_plot <- {img_id_r} + cells_sub <- cells[, colData(cells)$imageID == image_to_plot] + + p <- regionMap( + cells_sub, + region = "{region_col}" + ) + + ggsave("{output_path_posix}", plot = p, width = {width}, height = {height}) + """ + + tmp_path = None + cmd = [] + try: + with tempfile.NamedTemporaryFile(mode="w", suffix=".R", delete=False, encoding="utf-8") as tmp: + tmp.write(r_script) + tmp_path = tmp.name + + cmd = ["Rscript", tmp_path] + result = subprocess.run(cmd, capture_output=True, text=True, check=True, encoding="utf-8") + + return { + "command_executed": " ".join(cmd), + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": [str(output_plot)] + } + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(cmd), + "stdout": e.stdout, + "stderr": e.stderr, + "error": "R script execution failed." + } + finally: + if tmp_path and Path(tmp_path).exists(): + Path(tmp_path).unlink() + +@mcp.tool() +def lisa_heatmap( + input_rds: Path, + output_plot: Path, + region_col: str = "region", + width: int = 10, + height: int = 8 +) -> dict: + """ + Generate a heatmap of LISA values across identified regions. + + This function wraps the `lisaHeatmap()` function from `lisaClust`. + + Args: + input_rds: Path to an RDS file with lisaClust results and LISA values in metadata. + output_plot: Path to save the output heatmap (e.g., heatmap.png, heatmap.pdf). + region_col: Column name in colData containing region clusters. + width: Width of the output plot in inches. + height: Height of the output plot in inches. + """ + # Input validation + if not input_rds.exists(): + raise FileNotFoundError(f"Input file not found: {input_rds}") + if width <= 0 or height <= 0: + raise ValueError("Plot width and height must be positive.") + + # Ensure output directory exists + output_plot.parent.mkdir(parents=True, exist_ok=True) + + # Use .as_posix() for cross-platform path compatibility + input_path_posix = input_rds.as_posix() + output_path_posix = output_plot.as_posix() + + # Determine the R graphics device based on file extension + ext = output_plot.suffix.lower() + if ext == ".png": + device_open = f'png("{output_path_posix}", width = {width}, height = {height}, units = "in", res = 300)' + elif ext == ".pdf": + device_open = f'pdf("{output_path_posix}", width = {width}, height = {height})' + elif ext == ".svg": + device_open = f'svg("{output_path_posix}", width = {width}, height = {height})' + else: + # Default to png if extension is unknown or not supported by base R graphics + output_path_posix_with_ext = output_plot.with_suffix(".png").as_posix() + device_open = f'png("{output_path_posix_with_ext}", width = {width}, height = {height}, units = "in", res = 300)' + + r_script = f""" + library(lisaClust) + library(pheatmap) + library(SingleCellExperiment) + + cells <- readRDS("{input_path_posix}") + + # lisaHeatmap requires the LISA matrix, which should be in the metadata. + if (!"lisa" %in% names(metadata(cells))) {{ + stop("LISA values not found in metadata. Please run lisa_calculate first.") + }} + lisa_values <- metadata(cells)$lisa + + {device_open} + lisaHeatmap(cells, lisa = lisa_values, region = "{region_col}") + dev.off() + """ + + tmp_path = None + cmd = [] + try: + with tempfile.NamedTemporaryFile(mode="w", suffix=".R", delete=False, encoding="utf-8") as tmp: + tmp.write(r_script) + tmp_path = tmp.name + + cmd = ["Rscript", tmp_path] + result = subprocess.run(cmd, capture_output=True, text=True, check=True, encoding="utf-8") + + return { + "command_executed": " ".join(cmd), + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": [str(output_plot)] + } + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(cmd), + "stdout": e.stdout, + "stderr": e.stderr, + "error": "R script execution failed." + } + finally: + if tmp_path and Path(tmp_path).exists(): + Path(tmp_path).unlink() \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-lisaclust/app/bioconductor-lisaclust_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-lisaclust/app/bioconductor-lisaclust_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..99b83298805e7bbd1e16594d109b2a1554aa9046 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-lisaclust/app/bioconductor-lisaclust_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-lisaclust/app/bioconductor-lisaclust_server.py') +SERVER_NAME = 'biosci_bioconductor_lisaclust' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-lisaclust/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-lisaclust/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-lisaclust/app/requirements.txt @@ -0,0 +1 @@ + diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-lisaclust/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-lisaclust/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..6b5ccf07a1d68d868fca6a5ae63566a0cbfe846d --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-lisaclust/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-bioconductor-lisaclust: + build: . + image: mcp-bioconductor-lisaclust:latest + container_name: mcp-bioconductor-lisaclust + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=bioconductor-lisaclust + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-lisaclust/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-lisaclust/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..5b912c84ed80ef4befecd4169101fad7e1ed0132 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-lisaclust/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - bioconductor-lisaclust + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-lisaclust/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-lisaclust/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-lisaclust/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-mastr/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-mastr/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..7a616939e747f440f980604cbf5026f178643614 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-mastr/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install bioconductor-mastr via conda (e.g., from bioconda) +RUN conda install -c bioconda bioconductor-mastr -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY app/bioconductor-mastr_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/bioconductor-mastr_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/bioconductor-mastr_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-mastr/app/bioconductor-mastr_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-mastr/app/bioconductor-mastr_server.py new file mode 100644 index 0000000000000000000000000000000000000000..900182693bcab90d3966c78b0908e3ffb4a551cc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-mastr/app/bioconductor-mastr_server.py @@ -0,0 +1,200 @@ +import subprocess +import tempfile +from pathlib import Path +from typing import Optional, List, Union + +@mcp.tool() +def mastr_align( + fasta_input: str, + output_alignment: str, + iterations: int = 100, + temperature: float = 1.0, + cooling_rate: float = 0.9, + gap_open: float = -10.0, + gap_extend: float = -1.0, + use_structure: bool = True, + r_path: str = "Rscript" +): + """ + Perform Multiple Alignment of Structural RNAs (MASTR) using the Bioconductor mastr package. + + Args: + fasta_input: Path to the input FASTA file containing RNA sequences. + output_alignment: Path where the resulting alignment (FASTA format) will be saved. + iterations: Number of iterations for the simulated annealing (default: 100). + temperature: Initial temperature for simulated annealing (default: 1.0). + cooling_rate: Cooling rate for simulated annealing (default: 0.9). + gap_open: Penalty for opening a gap (default: -10.0). + gap_extend: Penalty for extending a gap (default: -1.0). + use_structure: Whether to incorporate secondary structure prediction (default: True). + r_path: Path to the Rscript executable (default: 'Rscript'). + """ + # Input validation + input_path = Path(fasta_input) + if not input_path.exists(): + return {"error": f"Input file not found: {fasta_input}"} + + output_path = Path(output_alignment) + output_path.parent.mkdir(parents=True, exist_ok=True) + + if iterations <= 0: + return {"error": "Iterations must be a positive integer."} + + if not (0 < cooling_rate < 1): + return {"error": "Cooling rate must be between 0 and 1."} + + # Create an R script to execute the mastr package + # Note: mastr is a Bioconductor package, so we use Rscript to interface with it. + r_script_content = f""" + library(mastr) + library(Biostrings) + + # Load sequences + seqs <- readRNAStringSet("{input_path.absolute()}") + + # Define parameters + params <- list( + iterations = {iterations}, + temp = {temperature}, + cooling = {cooling_rate}, + gapOpen = {gap_open}, + gapExtend = {gap_extend}, + useStructure = {"TRUE" if use_structure else "FALSE"} + ) + + # Run MASTR alignment + # Note: mastr() is the primary function in the Bioconductor package + alignment_result <- mastr(seqs, + iterations = params$iterations, + temp = params$temp, + cooling = params$cooling, + gapOpen = params$gapOpen, + gapExtend = params$gapExtend) + + # Write output + writeXStringSet(alignment_result, "{output_path.absolute()}") + """ + + with tempfile.NamedTemporaryFile(mode='w', suffix='.R', delete=False) as tmp: + tmp.write(r_script_content) + tmp_path = tmp.name + + try: + # Execute the R script + process = subprocess.run( + [r_path, tmp_path], + capture_output=True, + text=True, + check=True + ) + + return { + "command_executed": f"{r_path} {tmp_path}", + "stdout": process.stdout, + "stderr": process.stderr, + "output_files": [str(output_path.absolute())], + "status": "Success" + } + + except subprocess.CalledProcessError as e: + return { + "command_executed": f"{r_path} {tmp_path}", + "stdout": e.stdout, + "stderr": e.stderr, + "error": "R execution failed. Ensure 'mastr' and 'Biostrings' packages are installed in R.", + "status": "Error" + } + finally: + # Cleanup temporary R script + if Path(tmp_path).exists(): + Path(tmp_path).unlink() + +@mcp.tool() +def mastr_score_alignment( + alignment_file: str, + r_path: str = "Rscript" +): + """ + Calculate the structural conservation score for an existing RNA alignment using mastr metrics. + + Args: + alignment_file: Path to the aligned FASTA/Stockholm file. + r_path: Path to the Rscript executable. + """ + input_path = Path(alignment_file) + if not input_path.exists(): + return {"error": f"Alignment file not found: {alignment_file}"} + + r_script_content = f""" + library(mastr) + library(Biostrings) + + aln <- readRNAStringSet("{input_path.absolute()}") + # Calculate conservation score based on mastr's internal scoring + # This typically involves base-pairing probabilities and covariance + score <- mastr:::calculateScore(aln) # Accessing internal scoring if exported or via wrapper + cat("Alignment_Score:", score, "\\n") + """ + + with tempfile.NamedTemporaryFile(mode='w', suffix='.R', delete=False) as tmp: + tmp.write(r_script_content) + tmp_path = tmp.name + + try: + process = subprocess.run( + [r_path, tmp_path], + capture_output=True, + text=True, + check=True + ) + + return { + "command_executed": f"{r_path} {tmp_path}", + "stdout": process.stdout, + "stderr": process.stderr, + "status": "Success" + } + except subprocess.CalledProcessError as e: + return { + "error": str(e), + "stderr": e.stderr, + "status": "Error" + } + finally: + if Path(tmp_path).exists(): + Path(tmp_path).unlink() + +@mcp.tool() +def mastr_check_dependencies(r_path: str = "Rscript"): + """ + Verify if R and the required Bioconductor mastr package are installed and accessible. + + Args: + r_path: Path to the Rscript executable. + """ + r_script_content = """ + if (!requireNamespace("mastr", quietly = TRUE)) { + stop("Package 'mastr' not found. Please install it via BiocManager::install('mastr')") + } + if (!requireNamespace("Biostrings", quietly = TRUE)) { + stop("Package 'Biostrings' not found.") + } + cat("All dependencies are satisfied.\\n") + """ + + try: + process = subprocess.run( + [r_path, "-e", r_script_content], + capture_output=True, + text=True, + check=True + ) + return { + "status": "Ready", + "stdout": process.stdout + } + except Exception as e: + return { + "status": "Missing Dependencies", + "error": str(e) + } \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-mastr/app/bioconductor-mastr_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-mastr/app/bioconductor-mastr_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..eb2e264472de2c207ecdd88a874640e4e6b9960b --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-mastr/app/bioconductor-mastr_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-mastr/app/bioconductor-mastr_server.py') +SERVER_NAME = 'biosci_bioconductor_mastr' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-mastr/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-mastr/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-mastr/app/requirements.txt @@ -0,0 +1 @@ + diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-mastr/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-mastr/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..aad69a69c3c0d8f60c9082670992d86ee3fb50f2 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-mastr/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-bioconductor-mastr: + build: . + image: mcp-bioconductor-mastr:latest + container_name: mcp-bioconductor-mastr + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=bioconductor-mastr + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-mastr/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-mastr/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..63a854e34eec69ad376ee17e9f2cb9abe6add82b --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-mastr/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - bioconductor-mastr + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-mastr/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-mastr/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-mastr/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-matrixgenerics/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-matrixgenerics/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..3884a414339405a05ed35204259e33ccac923b21 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-matrixgenerics/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install bioconductor-matrixgenerics via conda (e.g., from bioconda) +RUN conda install -c bioconda bioconductor-matrixgenerics -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY app/bioconductor-matrixgenerics_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/bioconductor-matrixgenerics_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/bioconductor-matrixgenerics_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-matrixgenerics/app/bioconductor-matrixgenerics_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-matrixgenerics/app/bioconductor-matrixgenerics_server.py new file mode 100644 index 0000000000000000000000000000000000000000..c3463ffa08c93ca558b8e72d593237715090549e --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-matrixgenerics/app/bioconductor-matrixgenerics_server.py @@ -0,0 +1,223 @@ +import subprocess +import tempfile +from pathlib import Path +from typing import Literal, Optional + +# This is a placeholder for the MCP decorator. +# In a real MCP environment, this would be imported. +def mcp_tool_decorator_placeholder(*args, **kwargs): + def decorator(func): + return func + return decorator + +mcp = type("mcp", (), {"tool": mcp_tool_decorator_placeholder}) + + +# A comprehensive list of functions provided by the MatrixGenerics package. +MatrixGenericsOperation = Literal[ + "colAlls", "colAnyNAs", "colAnys", "colAvgsPerRow", "colCounts", "colCummaxs", + "colCummins", "colCumprods", "colCumsums", "colDiffs", "colIQRDiffs", "colIQRs", + "colLogSumExps", "colMadDiffs", "colMads", "colMaxs", "colMeans2", "colMedians", + "colMins", "colOrderStats", "colProds", "colQuantiles", "colRanges", "colRanks", + "colSdDiffs", "colSds", "colSums2", "colTabulates", "colVarDiffs", "colVars", + "colWeightedMads", "colWeightedMeans", "colWeightedMedians", "colWeightedSds", + "colWeightedVars", "rowAlls", "rowAnyNAs", "rowAnys", "rowAvgsPerCol", + "rowCounts", "rowCummaxs", "rowCummins", "rowCumprods", "rowCumsums", + "rowDiffs", "rowIQRDiffs", "rowIQRs", "rowLogSumExps", "rowMadDiffs", + "rowMads", "rowMaxs", "rowMeans2", "rowMedians", "rowMins", "rowOrderStats", + "rowProds", "rowQuantiles", "rowRanges", "rowRanks", "rowSdDiffs", "rowSds", + "rowSums2", "rowTabulates", "rowVarDiffs", "rowVars", "rowWeightedMads", + "rowWeightedMeans", "rowWeightedMedians", "rowWeightedSds", "rowWeightedVars" +] + +TiesMethod = Literal["average", "first", "last", "random", "max", "min"] + +@mcp.tool() +def perform_matrix_statistic( + input_matrix: Path, + operation: MatrixGenericsOperation, + output_filename: str, + na_rm: bool = True, + delimiter: Literal[",", "\t"] = "\t", + has_header: bool = True, + has_rownames: bool = True, + probs: Optional[str] = None, + weights: Optional[Path] = None, + ties_method: TiesMethod = "average", + value: Optional[float] = None, + lag: int = 1, + differences: int = 1, + which: Optional[int] = None, + preserve_shape: bool = False +) -> dict: + """ + Performs a specified summary statistic on a matrix using the MatrixGenerics R package. + + This tool serves as a wrapper around the bioconductor-matrixgenerics R library, + which provides generic functions for matrix statistics. It reads a matrix from a file, + applies the chosen statistical operation, and saves the result to a new file. + + Args: + input_matrix: Path to the input matrix file (e.g., CSV or TSV). + operation: The summary statistic to compute. + output_filename: The name for the output file that will be created. + na_rm: If True, NA/NaN values are removed before computation. + delimiter: Delimiter used in the input matrix file (',' or '\t'). + has_header: Whether the input file has a header row. + has_rownames: Whether the first column of the input file contains row names. + probs: Comma-separated string of probabilities for quantile functions (e.g., "0.25,0.5,0.75"). + weights: Path to a single-column file of numeric weights for weighted operations. + ties_method: Method for handling ties in ranking functions. + value: The specific value to count for '*Counts' functions. + lag: The lag for '*Diffs' functions. + differences: The number of differences for '*Diffs' functions. + which: The order statistic to extract for '*OrderStats' functions. + preserve_shape: For '*Ranks' functions, if TRUE the output has the same dimensions as the input. + + Returns: + A dictionary containing the executed command, stdout, stderr, and a list of output files. + """ + # --- Input Validation --- + if not input_matrix.is_file(): + raise FileNotFoundError(f"Input matrix file not found: {input_matrix}") + + if weights and not weights.is_file(): + raise FileNotFoundError(f"Weights file not found: {weights}") + + if not output_filename: + raise ValueError("Output filename cannot be empty.") + + # --- R Script Generation --- + r_script = f""" + # Load necessary library + if (!require("MatrixGenerics", quietly = TRUE)) {{ + stop("MatrixGenerics package is not installed.") + }} + + # --- Argument Parsing --- + args <- commandArgs(trailingOnly = TRUE) + input_file <- args[1] + output_file <- args[2] + operation <- args[3] + na_rm <- as.logical(args[4]) + sep <- args[5] + header <- as.logical(args[6]) + rownames_col <- if (as.logical(args[7])) 1 else NULL + probs_str <- args[8] + weights_file <- args[9] + ties_method <- args[10] + value_str <- args[11] + lag <- as.integer(args[12]) + differences <- as.integer(args[13]) + which_str <- args[14] + preserve_shape <- as.logical(args[15]) + + # --- Data Loading --- + tryCatch({{ + data <- read.delim(input_file, sep = sep, header = header, row.names = rownames_col, check.names = FALSE) + mat <- as.matrix(data) + }}, error = function(e) {{ + stop(paste("Error reading input matrix:", e$message)) + }}) + + # --- Dynamic Function Call --- + # Build the list of parameters + params <- list(x = mat) + + # Add common parameters if the function accepts them + func_args <- names(formals(operation)) + if ("na.rm" %in% func_args) params$na.rm <- na_rm + if ("ties.method" %in% func_args) params$ties.method <- ties_method + if ("value" %in% func_args && value_str != "NULL") params$value <- as.numeric(value_str) + if ("lag" %in% func_args) params$lag <- lag + if ("differences" %in% func_args) params$differences <- differences + if ("which" %in% func_args && which_str != "NULL") params$which <- as.integer(which_str) + if ("preserveShape" %in% func_args) params$preserveShape <- preserve_shape + + # Handle special parameters + if (grepl("Quantiles", operation) && probs_str != "NULL") {{ + params$probs <- as.numeric(strsplit(probs_str, ",")[[1]]) + }} + if (grepl("Weighted", operation) && weights_file != "NULL") {{ + tryCatch({{ + weights_vector <- read.table(weights_file, header = FALSE, colClasses = "numeric")[[1]] + params$w <- weights_vector + }}, error = function(e) {{ + stop(paste("Error reading weights file:", e$message)) + }}) + }} + + # Get the function from the package namespace + func <- get(operation, asNamespace("MatrixGenerics")) + + # Execute the function + result <- do.call(func, params) + + # --- Output Writing --- + # write.table is robust for vectors, matrices, and data.frames + write.table(result, file = output_file, sep = "\\t", quote = FALSE, col.names = NA) + """ + + with tempfile.TemporaryDirectory() as temp_dir: + temp_path = Path(temp_dir) + r_script_path = temp_path / "matrix_op.R" + with open(r_script_path, "w") as f: + f.write(r_script) + + output_path = Path.cwd() / output_filename + + # --- Command Construction --- + cmd = [ + "Rscript", + str(r_script_path), + str(input_matrix), + str(output_path), + operation, + str(na_rm).upper(), + "\\t" if delimiter == "\t" else ",", + str(has_header).upper(), + str(has_rownames).upper(), + probs if probs else "NULL", + str(weights) if weights else "NULL", + ties_method, + str(value) if value is not None else "NULL", + str(lag), + str(differences), + str(which) if which is not None else "NULL", + str(preserve_shape).upper() + ] + + # --- Subprocess Execution --- + try: + process = subprocess.run( + cmd, + check=True, + capture_output=True, + text=True, + ) + stdout = process.stdout + stderr = process.stderr + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(map(str, cmd)), + "stdout": e.stdout, + "stderr": e.stderr, + "output_files": [], + "error": "R script execution failed." + } + except FileNotFoundError: + return { + "command_executed": " ".join(map(str, cmd)), + "stdout": "", + "stderr": "Rscript command not found. Please ensure R is installed and in your PATH.", + "output_files": [], + "error": "Dependency not found." + } + + # --- Structured Result Return --- + return { + "command_executed": " ".join(map(str, cmd)), + "stdout": stdout, + "stderr": stderr, + "output_files": [str(output_path)] + } \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-matrixgenerics/app/bioconductor-matrixgenerics_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-matrixgenerics/app/bioconductor-matrixgenerics_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..31bc6ff51db51b08634ceb88ef3c3f381df653b3 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-matrixgenerics/app/bioconductor-matrixgenerics_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-matrixgenerics/app/bioconductor-matrixgenerics_server.py') +SERVER_NAME = 'biosci_bioconductor_matrixgenerics' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-matrixgenerics/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-matrixgenerics/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-matrixgenerics/app/requirements.txt @@ -0,0 +1 @@ + diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-matrixgenerics/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-matrixgenerics/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..7bd9323b314d29fa4bdf3a49442b1ef41fe55464 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-matrixgenerics/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-bioconductor-matrixgenerics: + build: . + image: mcp-bioconductor-matrixgenerics:latest + container_name: mcp-bioconductor-matrixgenerics + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=bioconductor-matrixgenerics + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-matrixgenerics/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-matrixgenerics/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..55a5066ba648a0a9d45d2f77c2a3fd79a0e2230f --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-matrixgenerics/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - bioconductor-matrixgenerics + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-matrixgenerics/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-matrixgenerics/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-matrixgenerics/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-mia/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-mia/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..550ada1f834d0528e040f3da5aab4014267c9673 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-mia/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install bioconductor-mia via conda (e.g., from bioconda) +RUN conda install -c bioconda bioconductor-mia -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY app/bioconductor-mia_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/bioconductor-mia_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/bioconductor-mia_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-mia/app/bioconductor-mia_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-mia/app/bioconductor-mia_server.py new file mode 100644 index 0000000000000000000000000000000000000000..24436ae7223c5a25b0e5e011f39fa177296fec8b --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-mia/app/bioconductor-mia_server.py @@ -0,0 +1,126 @@ +import subprocess +import tempfile +from pathlib import Path +from typing import Optional, List, Dict, Any + +@mcp.tool() +def run_mia_r_script( + r_script_content: str, + input_rds_path: Optional[Path] = None, + output_rds_path: Optional[Path] = None, + working_directory: Optional[Path] = None, +) -> Dict[str, Any]: + """ + Executes an R script that utilizes the bioconductor-mia package for microbiome analysis. + + This tool provides a generic interface to run arbitrary R code that is expected + to use the 'mia' Bioconductor package. It allows for loading an input R data + object (e.g., a SummarizedExperiment saved as .rds) and saving an output R data + object. + + Args: + r_script_content: A string containing the R script to be executed. + The script should assume 'mia' is loaded. + If `input_rds_path` is provided, the loaded object will + be available as `input_data`. + If `output_rds_path` is provided, the script should + assign the object to be saved to a variable named + `output_data`. + input_rds_path: Optional path to an R data object (.rds file) to be loaded + into the R environment before executing the script. + The object will be loaded as 'input_data'. + output_rds_path: Optional path where the R script should save an R data + object (e.g., a SummarizedExperiment) using saveRDS(). + The R script must assign the object to be saved to a + variable named 'output_data'. + working_directory: Optional path to a directory where the R script will be + executed. This can be useful for scripts that use + relative paths for additional input/output files. + + Returns: + A dictionary containing the command executed, stdout, stderr, and any + generated output files. + """ + if not r_script_content.strip(): + raise ValueError("r_script_content cannot be empty.") + + output_files: List[Path] = [] + + # Validate input_rds_path if provided + if input_rds_path: + if not input_rds_path.is_file(): + raise FileNotFoundError(f"Input RDS file not found: {input_rds_path}") + if not input_rds_path.suffix == ".rds": + raise ValueError(f"Input RDS file must have a '.rds' extension: {input_rds_path}") + + # Prepare output_rds_path if provided + if output_rds_path: + output_rds_path = output_rds_path.resolve() # Ensure absolute path + output_rds_path.parent.mkdir(parents=True, exist_ok=True) # Create parent dirs + if not output_rds_path.suffix == ".rds": + raise ValueError(f"Output RDS file must have a '.rds' extension: {output_rds_path}") + output_files.append(output_rds_path) + + # Create a temporary directory for the R script + with tempfile.TemporaryDirectory() as tmpdir: + tmpdir_path = Path(tmpdir) + + # Determine the actual working directory for the R process + actual_working_dir = working_directory if working_directory else tmpdir_path + actual_working_dir.mkdir(parents=True, exist_ok=True) # Ensure it exists + + r_script_file = tmpdir_path / "mia_script.R" + + # Construct the full R script content + full_r_script_lines = [ + "library(mia)", + "options(warn=-1)", # Suppress warnings for cleaner output + ] + + if input_rds_path: + full_r_script_lines.append(f"input_data <- readRDS('{input_rds_path.resolve()}')") + + full_r_script_lines.append(r_script_content) + + if output_rds_path: + # The user's script is expected to assign the result to 'output_data' + # We will then save 'output_data' to the specified path. + full_r_script_lines.append(f"if (exists('output_data')) {{ saveRDS(output_data, '{output_rds_path}') }} else {{ message('Warning: output_data variable not found, no RDS file saved.') }}") + + r_script_file.write_text("\n".join(full_r_script_lines)) + + command = ["Rscript", str(r_script_file)] + + try: + process = subprocess.run( + command, + cwd=actual_working_dir, + capture_output=True, + text=True, + check=True, + ) + stdout = process.stdout + stderr = process.stderr + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(command), + "stdout": e.stdout, + "stderr": e.stderr, + "error": f"Rscript execution failed with exit code {e.returncode}", + "output_files": [], + } + except FileNotFoundError: + return { + "command_executed": " ".join(command), + "stdout": "", + "stderr": "Error: Rscript command not found. Is R installed and in PATH?", + "error": "Rscript not found", + "output_files": [], + } + + return { + "command_executed": " ".join(command), + "stdout": stdout, + "stderr": stderr, + "output_files": [str(p) for p in output_files], + } \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-mia/app/bioconductor-mia_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-mia/app/bioconductor-mia_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..99b94b2b3585c0e2b9bcb5a51ef13e43100c1d51 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-mia/app/bioconductor-mia_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-mia/app/bioconductor-mia_server.py') +SERVER_NAME = 'biosci_bioconductor_mia' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-mia/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-mia/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-mia/app/requirements.txt @@ -0,0 +1 @@ + diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-mia/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-mia/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..448e04866675ab483f33e61b5ce5db50c0d53b89 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-mia/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-bioconductor-mia: + build: . + image: mcp-bioconductor-mia:latest + container_name: mcp-bioconductor-mia + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=bioconductor-mia + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-mia/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-mia/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..9c6e488272f2e53a2ddb32b7b1d99e6cb5ad0ac0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-mia/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - bioconductor-mia + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-mia/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-mia/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-mia/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-org.ce.eg.db/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-org.ce.eg.db/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..2d86d2a336872e24991c5f1dda25fd891c93cbc7 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-org.ce.eg.db/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install bioconductor-org.ce.eg.db via conda (e.g., from bioconda) +RUN conda install -c bioconda bioconductor-org.ce.eg.db -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY app/bioconductor-org.ce.eg.db_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/bioconductor-org.ce.eg.db_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/bioconductor-org.ce.eg.db_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-org.ce.eg.db/app/bioconductor-org.ce.eg.db_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-org.ce.eg.db/app/bioconductor-org.ce.eg.db_server.py new file mode 100644 index 0000000000000000000000000000000000000000..b6d9431ec27adc37733cb4d713f09617a74e2229 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-org.ce.eg.db/app/bioconductor-org.ce.eg.db_server.py @@ -0,0 +1,372 @@ +import subprocess +import textwrap +from pathlib import Path +from typing import List, Optional, Dict, Any + +# MCP decorator is not imported as per instructions, but assumed to be present. +# from mcp import tool as mcp_tool +# For the purpose of this script, we'll define a dummy decorator. +class mcp: + def tool(): + def decorator(f): + return f + return decorator + +@mcp.tool() +def select_annotations( + keys: List[str], + keytype: str, + columns: List[str], + output_file: str, + output_format: str = "tsv", +) -> Dict[str, Any]: + """ + Perform a complex query on the org.Ce.eg.db C. elegans annotation database using the select() method. + This returns a table of mappings between the provided keys and requested columns. + + Args: + keys: List of identifiers to look up (e.g., ["172469", "172470"] or ["skn-1", "gfp"]). + keytype: The type of the input keys (e.g., "ENTREZID", "SYMBOL", "WORMBASE", "ENSEMBL"). + columns: The annotation types to retrieve (e.g., ["GO", "EVIDENCE", "ONTOLOGY", "GENENAME"]). + output_file: Path to save the resulting table. + output_format: Format of the output file ("tsv" or "csv"). Defaults to "tsv". + """ + # Input validation + if not keys: + raise ValueError("The 'keys' list cannot be empty.") + if not keytype: + raise ValueError("The 'keytype' parameter must be specified.") + if not columns: + raise ValueError("The 'columns' list cannot be empty.") + + valid_formats = ["tsv", "csv"] + if output_format not in valid_formats: + raise ValueError(f"output_format must be one of {valid_formats}") + + out_path = Path(output_file).resolve() + out_path.parent.mkdir(parents=True, exist_ok=True) + + # Prepare R vectors safely + keys_str = ", ".join([f'"{k}"' for k in keys]) + cols_str = ", ".join([f'"{c}"' for c in columns]) + + # The R script uses f-string formatting. Literal curly braces for R code blocks + # must be escaped by doubling them (e.g., {{ and }}). + r_script = textwrap.dedent(f""" + suppressPackageStartupMessages(library(org.Ce.eg.db)) + suppressPackageStartupMessages(library(AnnotationDbi)) + + # Validate inputs in R + if (!("{keytype}" %in% keytypes(org.Ce.eg.db))) {{{{ + stop(paste("Invalid keytype:", "{keytype}")) + }}}} + + requested_cols <- c({cols_str}) + valid_cols <- columns(org.Ce.eg.db) + if (!all(requested_cols %in% valid_cols)) {{{{ + invalid_ones <- requested_cols[!(requested_cols %in% valid_cols)] + stop(paste("Invalid columns:", paste(invalid_ones, collapse=", "))) + }}}} + + res <- tryCatch({{{{ + AnnotationDbi::select( + org.Ce.eg.db, + keys = c({keys_str}), + columns = requested_cols, + keytype = "{keytype}" + ) + }}}}, error = function(e) {{{{ + stop(paste("Select failed:", e$message)) + }}}}) + + if ("{output_format}" == "tsv") {{{{ + write.table(res, file = "{out_path}", sep = "\\t", quote = FALSE, row.names = FALSE) + }}}} else {{{{ + write.csv(res, file = "{out_path}", quote = FALSE, row.names = FALSE) + }}}} + """) + + command_str = f"Rscript -e \"{r_script}\"" + try: + process = subprocess.run( + ["Rscript", "-e", r_script], + capture_output=True, + text=True, + check=True, + encoding='utf-8' + ) + return { + "command_executed": "Rscript -e 'AnnotationDbi::select(...)'", + "stdout": process.stdout, + "stderr": process.stderr, + "output_files": [str(out_path)] + } + except subprocess.CalledProcessError as e: + return { + "command_executed": "Rscript -e 'AnnotationDbi::select(...)'", + "stdout": e.stdout, + "stderr": e.stderr, + "error": f"R script execution failed. Stderr: {e.stderr}" + } + +@mcp.tool() +def map_ids( + keys: List[str], + keytype: str, + column: str, + multi_vals: str = "first", +) -> Dict[str, Any]: + """ + Map identifiers from one type to another using the mapIds() method. + This is optimized for 1-to-1 or 1-to-many mapping. + + Args: + keys: List of identifiers to map. + keytype: The type of the input keys (e.g., "WORMBASE"). + column: The type of identifier to map to (e.g., "SYMBOL"). + multi_vals: How to handle multiple matches. Options: "first", "list", "filter", "asNA", "CharacterList". + """ + if not keys: + raise ValueError("The 'keys' list cannot be empty.") + + valid_multi = ["first", "list", "filter", "asNA", "CharacterList"] + if multi_vals not in valid_multi: + raise ValueError(f"multi_vals must be one of {valid_multi}") + + keys_str = ", ".join([f'"{k}"' for k in keys]) + + r_script = textwrap.dedent(f""" + suppressPackageStartupMessages(library(org.Ce.eg.db)) + suppressPackageStartupMessages(library(AnnotationDbi)) + + res <- mapIds( + org.Ce.eg.db, + keys = c({keys_str}), + column = "{column}", + keytype = "{keytype}", + multiVals = "{multi_vals}" + ) + + # Convert to a structured key-value output for easy parsing + cat("---RESULT_START---\\n") + if (is.list(res) || is(res, "CharacterList")) {{{{ + for (n in names(res)) {{{{ + # Handle NULLs which represent no mapping + value <- if (is.null(res[[n]])) "NA" else paste(res[[n]], collapse=",") + cat(n, ":", value, "\\n") + }}}} + }}}} else {{{{ + for (n in names(res)) {{{{ + value <- if (is.na(res[n])) "NA" else res[n] + cat(n, ":", value, "\\n") + }}}} + }}}} + cat("---RESULT_END---\\n") + """) + + command_str = "Rscript -e 'AnnotationDbi::mapIds(...)'" + try: + process = subprocess.run( + ["Rscript", "-e", r_script], + capture_output=True, + text=True, + check=True, + encoding='utf-8' + ) + + # Parse the structured output from R + output_str = process.stdout + result_block = output_str.split("---RESULT_START---\n")[-1].split("---RESULT_END---")[0].strip() + mappings = {} + if result_block: + for line in result_block.split('\n'): + parts = line.split(':', 1) + if len(parts) == 2: + key = parts[0].strip() + val_str = parts[1].strip() + if val_str == "NA": + mappings[key] = None + else: + values = [v.strip() for v in val_str.split(',')] + if multi_vals in ["first", "asNA"] and len(values) == 1: + mappings[key] = values[0] + else: + mappings[key] = values + + return { + "command_executed": command_str, + "stdout": process.stdout, + "stderr": process.stderr, + "mappings": mappings + } + except subprocess.CalledProcessError as e: + return { + "command_executed": command_str, + "stdout": e.stdout, + "stderr": e.stderr, + "error": f"R script execution failed. Stderr: {e.stderr}" + } + +@mcp.tool() +def list_resources() -> Dict[str, Any]: + """ + List all available keytypes and columns (annotation types) in the org.Ce.eg.db package. + """ + r_script = textwrap.dedent(""" + suppressPackageStartupMessages(library(org.Ce.eg.db)) + cat("---COLUMNS_START---\\n") + cat(paste(columns(org.Ce.eg.db), collapse=", "), "\\n") + cat("---COLUMNS_END---\\n") + cat("---KEYTYPES_START---\\n") + cat(paste(keytypes(org.Ce.eg.db), collapse=", "), "\\n") + cat("---KEYTYPES_END---\\n") + """) + + command_str = "Rscript -e 'columns(...); keytypes(...)'" + try: + process = subprocess.run( + ["Rscript", "-e", r_script], + capture_output=True, + text=True, + check=True, + encoding='utf-8' + ) + + # Parse structured output + stdout_str = process.stdout + columns_str = stdout_str.split("---COLUMNS_START---\n")[1].split("---COLUMNS_END---")[0].strip() + keytypes_str = stdout_str.split("---KEYTYPES_START---\n")[1].split("---KEYTYPES_END---")[0].strip() + + return { + "command_executed": command_str, + "stdout": process.stdout, + "stderr": process.stderr, + "columns": columns_str.split(", ") if columns_str else [], + "keytypes": keytypes_str.split(", ") if keytypes_str else [] + } + except subprocess.CalledProcessError as e: + return { + "command_executed": command_str, + "stdout": e.stdout, + "stderr": e.stderr, + "error": f"R script execution failed. Stderr: {e.stderr}" + } + +@mcp.tool() +def get_db_metadata() -> Dict[str, Any]: + """ + Retrieve metadata about the org.Ce.eg.db package, such as version, species, and data sources. + """ + r_script = "suppressPackageStartupMessages(library(org.Ce.eg.db)); print(metadata(org.Ce.eg.db))" + command_str = f"Rscript -e \"{r_script}\"" + try: + process = subprocess.run( + ["Rscript", "-e", r_script], + capture_output=True, + text=True, + check=True, + encoding='utf-8' + ) + return { + "command_executed": command_str, + "stdout": process.stdout, + "stderr": process.stderr, + "metadata": process.stdout.strip().split('\n') + } + except subprocess.CalledProcessError as e: + return { + "command_executed": command_str, + "stdout": e.stdout, + "stderr": e.stderr, + "error": f"R script execution failed. Stderr: {e.stderr}" + } + +@mcp.tool() +def get_available_keys( + keytype: str, + pattern: Optional[str] = None, + limit: int = 100 +) -> Dict[str, Any]: + """ + Retrieve a list of all available keys for a specific keytype, optionally filtered by a pattern. + + Args: + keytype: The type of keys to retrieve (e.g., "SYMBOL"). + pattern: An optional regular expression pattern to filter keys. + limit: Maximum number of keys to return. Defaults to 100. + """ + if limit <= 0: + raise ValueError("Limit must be a positive integer.") + + pattern_arg = f', pattern = "{pattern}"' if pattern else "" + + r_script = textwrap.dedent(f""" + suppressPackageStartupMessages(library(org.Ce.eg.db)) + all_keys <- keys(org.Ce.eg.db, keytype = "{keytype}"{pattern_arg}) + cat(head(all_keys, {limit}), sep="\\n") + cat("\\n---TOTAL_COUNT:", length(all_keys), "---\\n") + """) + + command_str = f"Rscript -e 'keys(..., keytype=\"{keytype}\")'" + try: + process = subprocess.run( + ["Rscript", "-e", r_script], + capture_output=True, + text=True, + check=True, + encoding='utf-8' + ) + + # Robust parsing + output_str = process.stdout + if "---TOTAL_COUNT:" in output_str: + keys_part, total_part = output_str.split("---TOTAL_COUNT:") + keys_list = keys_part.strip().split("\n") if keys_part.strip() else [] + total_count = int(total_part.replace("---", "").strip()) + else: + keys_list = output_str.strip().split("\n") if output_str.strip() else [] + total_count = len(keys_list) + + return { + "command_executed": command_str, + "stdout": process.stdout, + "stderr": process.stderr, + "keys": keys_list, + "total_available": total_count + } + except subprocess.CalledProcessError as e: + return { + "command_executed": command_str, + "stdout": e.stdout, + "stderr": e.stderr, + "error": f"R script execution failed. Stderr: {e.stderr}" + } + +@mcp.tool() +def get_species_info() -> Dict[str, Any]: + """ + Get the species name for which this annotation package was designed. + """ + r_script = "suppressPackageStartupMessages(library(org.Ce.eg.db)); cat(species(org.Ce.eg.db))" + command_str = f"Rscript -e \"{r_script}\"" + try: + process = subprocess.run( + ["Rscript", "-e", r_script], + capture_output=True, + text=True, + check=True, + encoding='utf-8' + ) + return { + "command_executed": command_str, + "stdout": process.stdout, + "stderr": process.stderr, + "species": process.stdout.strip() + } + except subprocess.CalledProcessError as e: + return { + "command_executed": command_str, + "stdout": e.stdout, + "stderr": e.stderr, + "error": f"R script execution failed. Stderr: {e.stderr}" + } \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-org.ce.eg.db/app/bioconductor-org.ce.eg.db_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-org.ce.eg.db/app/bioconductor-org.ce.eg.db_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..80ddfab2fbff4acf9c62ba0c7fe1a94a47b37b9b --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-org.ce.eg.db/app/bioconductor-org.ce.eg.db_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-org.ce.eg.db/app/bioconductor-org.ce.eg.db_server.py') +SERVER_NAME = 'biosci_bioconductor_org_ce_eg_db' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-org.ce.eg.db/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-org.ce.eg.db/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-org.ce.eg.db/app/requirements.txt @@ -0,0 +1 @@ + diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-org.ce.eg.db/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-org.ce.eg.db/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..b0aca5cb848af22025e1277befdd8c28f521b8bd --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-org.ce.eg.db/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-bioconductor-org.ce.eg.db: + build: . + image: mcp-bioconductor-org.ce.eg.db:latest + container_name: mcp-bioconductor-org.ce.eg.db + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=bioconductor-org.ce.eg.db + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-org.ce.eg.db/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-org.ce.eg.db/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..e4b02b7acc6a22c38fee1f3d8401b748f0cfde91 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-org.ce.eg.db/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - bioconductor-org.ce.eg.db + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-org.ce.eg.db/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-org.ce.eg.db/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-org.ce.eg.db/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-org.hs.eg.db/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-org.hs.eg.db/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..4063797cda67f7442fa6031a699b1ed32b5b26f8 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-org.hs.eg.db/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install bioconductor-org.hs.eg.db via conda (e.g., from bioconda) +RUN conda install -c bioconda bioconductor-org.hs.eg.db -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY app/bioconductor-org.hs.eg.db_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/bioconductor-org.hs.eg.db_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/bioconductor-org.hs.eg.db_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-org.hs.eg.db/app/bioconductor-org.hs.eg.db_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-org.hs.eg.db/app/bioconductor-org.hs.eg.db_server.py new file mode 100644 index 0000000000000000000000000000000000000000..5b0ed9eb3ae10b49528a230d7f19c22fdec2667b --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-org.hs.eg.db/app/bioconductor-org.hs.eg.db_server.py @@ -0,0 +1,189 @@ +import subprocess +import tempfile +import json +from pathlib import Path +from typing import List, Optional, Union + +def _run_r_command(script: str): + """Helper to execute R code and handle errors.""" + try: + # Check if R is installed + subprocess.run(["Rscript", "--version"], capture_output=True, check=True) + except (subprocess.CalledProcessError, FileNotFoundError): + return { + "error": "Rscript not found. Please ensure R and Bioconductor package 'org.Hs.eg.db' are installed.", + "stdout": "", + "stderr": "" + } + + with tempfile.NamedTemporaryFile(mode='w', suffix='.R', delete=False) as tmp: + tmp.write("suppressPackageStartupMessages(library(org.Hs.eg.db))\n") + tmp.write(script) + tmp_path = tmp.name + + try: + result = subprocess.run( + ["Rscript", tmp_path], + capture_output=True, + text=True, + check=True + ) + return { + "command_executed": f"Rscript {tmp_path}", + "stdout": result.stdout, + "stderr": result.stderr, + "success": True + } + except subprocess.CalledProcessError as e: + return { + "command_executed": f"Rscript {tmp_path}", + "stdout": e.stdout, + "stderr": e.stderr, + "success": False, + "error": str(e) + } + finally: + if Path(tmp_path).exists(): + Path(tmp_path).unlink() + +@mcp.tool() +def org_hs_eg_db_select( + keys: List[str], + columns: List[str], + keytype: str = "ENTREZID" +): + """ + Retrieve data from org.Hs.eg.db using the select method. + + Args: + keys: The identifiers to look up (e.g., ['7157', '10']). + columns: The types of data to retrieve (e.g., ['SYMBOL', 'GENENAME']). + keytype: The type of identifier passed in 'keys' (default: 'ENTREZID'). + """ + if not keys: + return {"error": "Keys list cannot be empty"} + if not columns: + return {"error": "Columns list cannot be empty"} + + # Format keys and columns for R + keys_r = 'c("' + '","'.join(keys) + '")' + cols_r = 'c("' + '","'.join(columns) + '")' + + script = f""" + res <- tryCatch({{ + select(org.Hs.eg.db, keys = {keys_r}, columns = {cols_r}, keytype = "{keytype}") + }}, error = function(e) {{ + stop(e$message) + }}) + write.csv(res, row.names = FALSE) + """ + + result = _run_r_command(script) + return result + +@mcp.tool() +def org_hs_eg_db_map_ids( + keys: List[str], + column: str, + keytype: str = "ENTREZID", + multi_vals: str = "first" +): + """ + Map identifiers 1-to-1 using the mapIds method. + + Args: + keys: The identifiers to look up. + column: The single column to retrieve. + keytype: The type of identifier passed in 'keys'. + multi_vals: How to handle multiple matches ('first', 'list', 'filter', 'asNA', 'CharacterList'). + """ + valid_multi = ["first", "list", "filter", "asNA", "CharacterList"] + if multi_vals not in valid_multi: + return {"error": f"multi_vals must be one of {valid_multi}"} + + keys_r = 'c("' + '","'.join(keys) + '")' + + script = f""" + res <- mapIds(org.Hs.eg.db, keys = {keys_r}, column = "{column}", + keytype = "{keytype}", multiVals = "{multi_vals}") + # Convert named vector to JSON-like output + cat(jsonlite::toJSON(as.list(res), auto_unbox = TRUE)) + """ + # Note: requires jsonlite in R for this specific tool's formatting + result = _run_r_command(script) + return result + +@mcp.tool() +def org_hs_eg_db_get_keys( + keytype: str = "ENTREZID", + pattern: str = "", + column: str = "" +): + """ + Retrieve all keys of a specific type from the database. + + Args: + keytype: The type of keys to retrieve. + pattern: Optional grep pattern to filter keys. + column: Optional column to filter keys by. + """ + filter_logic = "" + if pattern: + filter_logic = f', pattern = "{pattern}"' + if column: + filter_logic += f', column = "{column}"' + + script = f""" + res <- keys(org.Hs.eg.db, keytype = "{keytype}"{filter_logic}) + cat(res, sep="\\n") + """ + + result = _run_r_command(script) + return result + +@mcp.tool() +def org_hs_eg_db_metadata(): + """ + Retrieve metadata about the org.Hs.eg.db package, including available columns and keytypes. + """ + script = """ + cat("COLUMNS:\\n") + cat(columns(org.Hs.eg.db), sep=", ") + cat("\\n\\nKEYTYPES:\\n") + cat(keytypes(org.Hs.eg.db), sep=", ") + cat("\\n\\nSPECIES:\\n") + cat(species(org.Hs.eg.db)) + """ + + result = _run_r_command(script) + return result + +@mcp.tool() +def org_hs_eg_db_query_by_symbol( + symbols: List[str], + columns: List[str] = ["ENTREZID", "ENSEMBL", "GENENAME"] +): + """ + Convenience tool to map Gene Symbols to other identifiers. + + Args: + symbols: List of gene symbols (e.g., ['TP53', 'BRCA1']). + columns: Data columns to return. + """ + if not symbols: + return {"error": "Symbols list cannot be empty"} + + symbols_r = 'c("' + '","'.join(symbols) + '")' + cols_r = 'c("' + '","'.join(columns) + '")' + + script = f""" + res <- tryCatch({{ + select(org.Hs.eg.db, keys = {symbols_r}, columns = {cols_r}, keytype = "SYMBOL") + }}, error = function(e) {{ + stop(e$message) + }}) + write.csv(res, row.names = FALSE) + """ + + result = _run_r_command(script) + return result \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-org.hs.eg.db/app/bioconductor-org.hs.eg.db_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-org.hs.eg.db/app/bioconductor-org.hs.eg.db_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..a0e95f1557e28a22ad96066d7aeb17cb217cbca6 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-org.hs.eg.db/app/bioconductor-org.hs.eg.db_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-org.hs.eg.db/app/bioconductor-org.hs.eg.db_server.py') +SERVER_NAME = 'biosci_bioconductor_org_hs_eg_db' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-org.hs.eg.db/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-org.hs.eg.db/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-org.hs.eg.db/app/requirements.txt @@ -0,0 +1 @@ + diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-org.hs.eg.db/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-org.hs.eg.db/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..4eb289ab819090371a3aacbde6c3d48a7e26bd78 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-org.hs.eg.db/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-bioconductor-org.hs.eg.db: + build: . + image: mcp-bioconductor-org.hs.eg.db:latest + container_name: mcp-bioconductor-org.hs.eg.db + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=bioconductor-org.hs.eg.db + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-org.hs.eg.db/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-org.hs.eg.db/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..2ddb7785c9c4ce5c193d1ada773c1d53f4a86a89 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-org.hs.eg.db/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - bioconductor-org.hs.eg.db + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-org.hs.eg.db/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-org.hs.eg.db/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-org.hs.eg.db/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-org.mm.eg.db/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-org.mm.eg.db/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..4bde49a4d261b08d5cc4b18d0a28d63809e09d28 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-org.mm.eg.db/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install bioconductor-org.mm.eg.db via conda (e.g., from bioconda) +RUN conda install -c bioconda bioconductor-org.mm.eg.db -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY app/bioconductor-org.mm.eg.db_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/bioconductor-org.mm.eg.db_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/bioconductor-org.mm.eg.db_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-org.mm.eg.db/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-org.mm.eg.db/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-org.mm.eg.db/app/requirements.txt @@ -0,0 +1 @@ + diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-poem/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-poem/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..553c1706098fba4d10920b0f15ee917c7b3a6ee8 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-poem/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install bioconductor-poem via conda (e.g., from bioconda) +RUN conda install -c bioconda bioconductor-poem -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY app/bioconductor-poem_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/bioconductor-poem_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/bioconductor-poem_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-poem/app/bioconductor-poem_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-poem/app/bioconductor-poem_server.py new file mode 100644 index 0000000000000000000000000000000000000000..c6b5a6db11efae22bb62fd5b2c42ca4e0f62e4a9 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-poem/app/bioconductor-poem_server.py @@ -0,0 +1,229 @@ +import subprocess +import tempfile +from pathlib import Path +from typing import Optional, Dict +import logging + +# Configure logging +logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') +logger = logging.getLogger(__name__) + +# This is a mock decorator for development and testing purposes. +# In a real MCP environment, this would be provided by the MCP framework. +class mcp: + @staticmethod + def tool(): + def decorator(func): + return func + return decorator + +@mcp.tool() +def poem_evaluate( + clustering_results: Path, + output_prefix: str, + ground_truth_labels: Optional[Path] = None, + spatial_coordinates: Optional[Path] = None, + embedding_coordinates: Optional[Path] = None, + calculate_external_metrics: bool = True, + calculate_internal_metrics: bool = True, + calculate_spatial_metrics: bool = True, + generate_spatial_error_plot: bool = False, +) -> Dict: + """ + Run POpulation-based Evaluation Metrics (poem) analysis on clustering or spatial domain results. + + This tool serves as a wrapper for the Bioconductor 'poem' R package. It generates and executes an R script + to calculate various evaluation metrics based on the provided input files. Since 'poem' is an R library + and not a command-line tool, this function synthesizes a command-line-like interface to its core functionalities. + + Args: + clustering_results: Path to a single-column file (CSV/TSV, no header) containing cluster assignments for each cell/spot. + output_prefix: A string prefix for all output files (e.g., 'my_analysis'). + ground_truth_labels: Optional path to a single-column file with ground truth labels for external validation. + spatial_coordinates: Optional path to a two-column file (CSV/TSV, no header) with X and Y spatial coordinates. + embedding_coordinates: Optional path to a multi-column file (CSV/TSV, no header) with embedding coordinates (e.g., UMAP, PCA). + calculate_external_metrics: If True, calculate external metrics like ARI. Requires `ground_truth_labels`. + calculate_internal_metrics: If True, calculate internal metrics like Silhouette score. Requires `embedding_coordinates` or `spatial_coordinates`. + calculate_spatial_metrics: If True, calculate spatial-aware metrics. Requires `spatial_coordinates`. + generate_spatial_error_plot: If True, generate a plot visualizing spatial errors. Requires `spatial_coordinates` and `ground_truth_labels`. + + Returns: + A dictionary containing the executed command, stdout, stderr, and a list of output file paths. + """ + # --- 1. Input Validation --- + if not clustering_results.is_file(): + raise FileNotFoundError(f"Clustering results file not found: {clustering_results}") + if ground_truth_labels and not ground_truth_labels.is_file(): + raise FileNotFoundError(f"Ground truth labels file not found: {ground_truth_labels}") + if spatial_coordinates and not spatial_coordinates.is_file(): + raise FileNotFoundError(f"Spatial coordinates file not found: {spatial_coordinates}") + if embedding_coordinates and not embedding_coordinates.is_file(): + raise FileNotFoundError(f"Embedding coordinates file not found: {embedding_coordinates}") + + if calculate_external_metrics and not ground_truth_labels: + raise ValueError("`ground_truth_labels` must be provided to calculate external metrics.") + if (calculate_spatial_metrics or generate_spatial_error_plot) and not spatial_coordinates: + raise ValueError("`spatial_coordinates` must be provided for spatial metrics or plots.") + if calculate_internal_metrics and not (embedding_coordinates or spatial_coordinates): + raise ValueError("Either `embedding_coordinates` or `spatial_coordinates` must be provided for internal metrics.") + + # --- 2. Temporary Directory and Script Generation --- + with tempfile.TemporaryDirectory() as temp_dir_str: + temp_dir = Path(temp_dir_str) + r_script_path = temp_dir / "run_poem.R" + output_metrics_path = temp_dir / f"{output_prefix}_metrics.csv" + output_plot_path = temp_dir / f"{output_prefix}_spatial_error.png" + + # NOTE: The R function names (`calculateExternalMetrics`, `plotSpatialErrors`, etc.) are plausible + # placeholders based on the package's description, as the exact function names are not available + # in the provided documentation. This script demonstrates a robust way to wrap such a library. + r_script_content = f""" + options(warn = -1) + suppressPackageStartupMessages({{ + library(poem) + library(SpatialExperiment) + library(SummarizedExperiment) + library(ggplot2) + }}) + + cat("--- Loading Data ---\\n") + clusters <- read.csv('{clustering_results.resolve()}', header = FALSE, col.names = c('cluster'))$cluster + + metrics_list <- list() + + # --- Create a base data object (SummarizedExperiment or SpatialExperiment) --- + sce <- NULL + if (!is.null('{spatial_coordinates.resolve() if spatial_coordinates else ''}') || !is.null('{embedding_coordinates.resolve() if embedding_coordinates else ''}')) {{ + counts <- matrix(0, nrow = 1, ncol = length(clusters)) # Dummy assay + colData <- data.frame(cluster_id = as.factor(clusters)) + + if (!is.null('{spatial_coordinates.resolve() if spatial_coordinates else ''}')) {{ + spatial_coords <- as.matrix(read.csv('{spatial_coordinates.resolve()}', header = FALSE)) + colnames(spatial_coords) <- c('x', 'y') + sce <- SpatialExperiment(assays = list(counts = counts), colData = colData, spatialCoords = spatial_coords) + cat("Created SpatialExperiment object.\\n") + }} else {{ + sce <- SummarizedExperiment(assays = list(counts = counts), colData = colData) + embedding_coords <- as.matrix(read.csv('{embedding_coordinates.resolve()}', header = FALSE)) + reducedDim(sce, "embedding") <- embedding_coords + cat("Created SummarizedExperiment object with embeddings.\\n") + }} + }} + + # --- External Metrics --- + if ({str(calculate_external_metrics).upper()}) {{ + cat("\\n--- Calculating External Metrics ---\\n") + ground_truth <- read.csv('{ground_truth_labels.resolve()}', header = FALSE, col.names = c('label'))$label + # Using a known function from 'aricode' which 'poem' imports + external_metrics <- aricode::clustComp(clusters, ground_truth) + metrics_list[['external']] <- external_metrics + cat("External metrics calculated.\\n") + }} + + # --- Internal & Spatial Metrics --- + if (!is.null(sce)) {{ + if ({str(calculate_internal_metrics).upper()}) {{ + cat("\\n--- Calculating Internal Metrics ---\\n") + # Using a known function from 'bluster' which 'poem' imports + dist_matrix <- dist(if (!is.null(reducedDim(sce, "embedding"))) reducedDim(sce, "embedding") else spatialCoords(sce)) + silhouette_scores <- cluster::silhouette(as.integer(sce$cluster_id), dist_matrix) + # Handle potential NaN from single-cluster cases + mean_silhouette <- if(is.nan(mean(silhouette_scores[,3]))) 0 else mean(silhouette_scores[,3]) + metrics_list[['internal']] <- list(mean_silhouette = mean_silhouette) + cat("Internal metrics calculated.\\n") + }} + if ({str(calculate_spatial_metrics).upper()} && inherits(sce, "SpatialExperiment")) {{ + cat("\\n--- Calculating Spatial Metrics ---\\n") + # Hypothetical function call for a spatial metric like Moran's I + # This requires more setup (spatial weights matrix), so we'll use a placeholder. + # In a real scenario, this would call a specific function from 'poem' or 'spdep'. + metrics_list[['spatial']] <- list(morans_i_placeholder = runif(1)) + cat("Spatial metrics calculated (using placeholder).\\n") + }} + }} + + # --- Combine and Save Metrics --- + if (length(metrics_list) > 0) {{ + cat("\\n--- Saving Metrics ---\\n") + results_df <- data.frame( + metric = unlist(lapply(metrics_list, names)), + value = unlist(metrics_list), + category = rep(names(metrics_list), sapply(metrics_list, length)) + ) + write.csv(results_df, file = '{output_metrics_path}', row.names = FALSE) + cat("Metrics saved to {output_metrics_path}\\n") + }} + + # --- Visualization --- + if ({str(generate_spatial_error_plot).upper()} && inherits(sce, "SpatialExperiment") && !is.null('{ground_truth_labels.resolve() if ground_truth_labels else ''}')) {{ + cat("\\n--- Generating Spatial Error Plot ---\\n") + ground_truth <- read.csv('{ground_truth_labels.resolve()}', header = FALSE, col.names = c('label'))$label + colData(sce)$ground_truth <- as.factor(ground_truth) + + # Create a plot showing disagreement + plot_data <- as.data.frame(cbind(spatialCoords(sce), colData(sce))) + plot_data$agreement <- ifelse(plot_data$cluster_id == plot_data$ground_truth, "Agree", "Disagree") + + p <- ggplot(plot_data, aes(x = x, y = y, color = agreement)) + + geom_point(size = 2) + + scale_color_manual(values = c("Agree" = "gray", "Disagree" = "red")) + + theme_bw() + + labs(title = "Spatial Disagreement Plot", x = "X coordinate", y = "Y coordinate") + + ggsave('{output_plot_path}', plot = p, width = 8, height = 6) + cat("Spatial error plot saved to {output_plot_path}\\n") + }} + + cat("\\n--- poem analysis script finished ---\\n") + """ + + with open(r_script_path, "w") as f: + f.write(r_script_content) + + # --- 3. Subprocess Execution --- + command = ["Rscript", str(r_script_path)] + command_str = " ".join(map(str, command)) + logger.info(f"Executing command: {command_str}") + + try: + result = subprocess.run( + command, + capture_output=True, + text=True, + check=True, + cwd=temp_dir, + ) + except FileNotFoundError: + # This error is for when 'Rscript' itself is not found + raise RuntimeError("Rscript executable not found. Please ensure R is installed and in your system's PATH.") + except subprocess.CalledProcessError as e: + # This error is for when the R script fails + error_message = ( + f"R script execution failed with exit code {e.returncode}.\n" + f"Stderr:\n{e.stderr}\n" + f"Stdout:\n{e.stdout}" + ) + logger.error(error_message) + # Return a structured error in the MCP format + return { + "command_executed": command_str, + "stdout": e.stdout, + "stderr": e.stderr, + "error": "R script execution failed.", + "return_code": e.returncode, + "output_files": [] + } + + # --- 4. Collect and Return Structured Output --- + output_files = [] + if output_metrics_path.exists() and output_metrics_path.stat().st_size > 0: + output_files.append(str(output_metrics_path)) + if output_plot_path.exists() and output_plot_path.stat().st_size > 0: + output_files.append(str(output_plot_path)) + + return { + "command_executed": command_str, + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": output_files, + } \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-poem/app/bioconductor-poem_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-poem/app/bioconductor-poem_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..d06f7831da87db06169b82a15001a6313c084b88 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-poem/app/bioconductor-poem_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-poem/app/bioconductor-poem_server.py') +SERVER_NAME = 'biosci_bioconductor_poem' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-poem/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-poem/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-poem/app/requirements.txt @@ -0,0 +1 @@ + diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-poem/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-poem/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..0f7e7e8be4a0b810e1a47921ac69940fb287553a --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-poem/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-bioconductor-poem: + build: . + image: mcp-bioconductor-poem:latest + container_name: mcp-bioconductor-poem + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=bioconductor-poem + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-poem/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-poem/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..6b3c239e7ecff5c3697ec3fc78557bf7babec05f --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-poem/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - bioconductor-poem + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-poem/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-poem/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-poem/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-rhdf5filters/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-rhdf5filters/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..70ad418d4a9082937d70718a7a788888f0f4525b --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-rhdf5filters/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install bioconductor-rhdf5filters via conda (e.g., from bioconda) +RUN conda install -c bioconda bioconductor-rhdf5filters -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY app/bioconductor-rhdf5filters_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/bioconductor-rhdf5filters_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/bioconductor-rhdf5filters_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-rhdf5filters/app/bioconductor-rhdf5filters_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-rhdf5filters/app/bioconductor-rhdf5filters_server.py new file mode 100644 index 0000000000000000000000000000000000000000..7ec9a2cfee279b90853b33fbe673f81be3998eab --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-rhdf5filters/app/bioconductor-rhdf5filters_server.py @@ -0,0 +1,70 @@ +import subprocess +import tempfile +import shutil +from pathlib import Path +from typing import Dict, Any, List + +# MCP decorator is commented out as per instructions +# @mcp.tool() +def check_installation() -> Dict[str, Any]: + """ + Verifies the installation of the bioconductor-rhdf5filters R library. + + bioconductor-rhdf5filters is an R library that provides HDF5 compression + filters and does not have a direct command-line interface. This function + executes a simple R script to confirm that the library is installed and + can be loaded in the R environment, and it reports the installed version. + """ + # Pre-flight check for Rscript executable + if not shutil.which("Rscript"): + raise FileNotFoundError("Rscript executable not found in PATH. R is required to run this tool.") + + r_script_content = """ + if (!requireNamespace("rhdf5filters", quietly = TRUE)) { + cat("Error: The 'rhdf5filters' package is not installed.\\n", file = stderr()) + quit(status = 1) + } + ver <- as.character(packageVersion("rhdf5filters")) + cat(paste0("Successfully loaded 'rhdf5filters' version: ", ver, "\\n")) + quit(status = 0) + """ + + command_executed = "" + script_path = "" + try: + with tempfile.NamedTemporaryFile(mode='w', delete=False, suffix=".R", encoding='utf-8') as tmp_r_script: + script_path = tmp_r_script.name + tmp_r_script.write(r_script_content) + + cmd: List[str] = ["Rscript", script_path] + command_executed = " ".join(cmd) + + process = subprocess.run( + cmd, + capture_output=True, + text=True, + check=True, + encoding='utf-8' + ) + + return { + "command_executed": command_executed, + "stdout": process.stdout, + "stderr": process.stderr, + "output_files": [] + } + + except subprocess.CalledProcessError as e: + # This will catch the case where the R script exits with a non-zero status + # (e.g., package not installed) and return a structured error. + return { + "command_executed": command_executed, + "stdout": e.stdout, + "stderr": e.stderr, + "return_code": e.returncode, + "error": "R script execution failed. The 'rhdf5filters' package may not be installed or accessible." + } + finally: + # Ensure the temporary file is cleaned up + if script_path and Path(script_path).exists(): + Path(script_path).unlink() diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-rhdf5filters/app/bioconductor-rhdf5filters_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-rhdf5filters/app/bioconductor-rhdf5filters_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..e2d287bc648131166c898cb78ab7cfacf44a55ec --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-rhdf5filters/app/bioconductor-rhdf5filters_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-rhdf5filters/app/bioconductor-rhdf5filters_server.py') +SERVER_NAME = 'biosci_bioconductor_rhdf5filters' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-rhdf5filters/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-rhdf5filters/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-rhdf5filters/app/requirements.txt @@ -0,0 +1 @@ + diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-rhdf5filters/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-rhdf5filters/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..81832d0f80640700360671a525f10b97f2382efa --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-rhdf5filters/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-bioconductor-rhdf5filters: + build: . + image: mcp-bioconductor-rhdf5filters:latest + container_name: mcp-bioconductor-rhdf5filters + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=bioconductor-rhdf5filters + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-rhdf5filters/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-rhdf5filters/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..a0adbb1473ea9468bd807b23ff418eeac7f03f44 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-rhdf5filters/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - bioconductor-rhdf5filters + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-rhdf5filters/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-rhdf5filters/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-rhdf5filters/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-rhtslib/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-rhtslib/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..06606fbc87c7cc914c960f63ba38650caa95bbc6 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-rhtslib/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install bioconductor-rhtslib via conda (e.g., from bioconda) +RUN conda install -c bioconda bioconductor-rhtslib -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY app/bioconductor-rhtslib_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/bioconductor-rhtslib_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/bioconductor-rhtslib_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-rhtslib/app/bioconductor-rhtslib_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-rhtslib/app/bioconductor-rhtslib_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..a9d03b2d72645bf035a722ac60db7a736f5ff279 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-rhtslib/app/bioconductor-rhtslib_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-rhtslib/app/bioconductor-rhtslib_server.py') +SERVER_NAME = 'biosci_bioconductor_rhtslib' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-rhtslib/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-rhtslib/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-rhtslib/app/requirements.txt @@ -0,0 +1 @@ + diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-rhtslib/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-rhtslib/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..e3490324c687de6f1d638054610292218df8d093 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-rhtslib/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-bioconductor-rhtslib: + build: . + image: mcp-bioconductor-rhtslib:latest + container_name: mcp-bioconductor-rhtslib + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=bioconductor-rhtslib + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-rhtslib/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-rhtslib/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..8d9fd1f10209694d0af1705cd86798274dadc35c --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-rhtslib/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - bioconductor-rhtslib + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-rhtslib/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-rhtslib/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-rhtslib/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-rsubread/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-rsubread/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..e4dec6ab2174115a3f1326b6fd37bc802e9c6b00 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-rsubread/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install bioconductor-rsubread via conda (e.g., from bioconda) +RUN conda install -c bioconda bioconductor-rsubread -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY app/bioconductor-rsubread_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/bioconductor-rsubread_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/bioconductor-rsubread_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-rsubread/app/bioconductor-rsubread_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-rsubread/app/bioconductor-rsubread_server.py new file mode 100644 index 0000000000000000000000000000000000000000..80e5fd6594e70b8effa2a3f95629f5cb3b394c5f --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-rsubread/app/bioconductor-rsubread_server.py @@ -0,0 +1,435 @@ +import subprocess +import tempfile +from pathlib import Path +from typing import List, Optional, Union + +# Helper function to execute Rscript commands +def _run_r_script(r_code: str, tool_name: str) -> dict: + """ + Executes an R script using Rscript. + + Args: + r_code: The R code string to execute. + tool_name: The name of the Rsubread function being called, for error reporting. + + Returns: + A dictionary containing command execution details, stdout, stderr, and output files. + """ + r_script_content = f"library(Rsubread)\n{r_code}" + + with tempfile.NamedTemporaryFile(mode="w", suffix=".R", delete=False) as r_script_file: + r_script_file.write(r_script_content) + r_script_path = Path(r_script_file.name) + + command = ["Rscript", str(r_script_path)] + + try: + process = subprocess.run( + command, + capture_output=True, + text=True, + check=True + ) + stdout = process.stdout + stderr = process.stderr + + return { + "command_executed": " ".join(command), + "stdout": stdout, + "stderr": stderr, + "output_files": [], # To be populated by individual tool functions + } + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(command), + "stdout": e.stdout, + "stderr": e.stderr, + "error": f"Rsubread {tool_name} failed with exit code {e.returncode}", + "output_files": [], + } + except FileNotFoundError: + return { + "command_executed": " ".join(command), + "stdout": "", + "stderr": "Rscript command not found. Is R installed and in PATH?", + "error": "Rscript not found", + "output_files": [], + } + finally: + # Clean up temporary R script file + r_script_path.unlink(missing_ok=True) + + +@mcp.tool() +def build_index( + index_name: Path, + reference_fasta: Path, + gff_file: Optional[Path] = None, + threads: int = 1, + memory: Optional[int] = None, +) -> dict: + """ + Builds a Subread index for read alignment. + + Args: + index_name: Prefix for the output index files. The actual index files will be + created with this prefix (e.g., index_name.00.b.array, etc.). + reference_fasta: Path to the reference genome FASTA file. + gff_file: Optional path to a GTF/GFF annotation file for splice-aware indexing. + threads: Number of CPU threads to use. Must be at least 1. + memory: Maximum memory (in MB) to use. Must be a positive integer if provided. + + Returns: + A dictionary containing command execution details, stdout, stderr, and output files. + """ + # Input validation + if not reference_fasta.is_file(): + raise ValueError(f"Reference FASTA file not found: {reference_fasta}") + if gff_file and not gff_file.is_file(): + raise ValueError(f"GFF/GTF file not found: {gff_file}") + if threads < 1: + raise ValueError("Number of threads must be at least 1.") + if memory is not None and memory <= 0: + raise ValueError("Memory limit must be a positive integer.") + + r_code_params = [ + f'indexName="{index_name.resolve()}"', + f'reference="{reference_fasta.resolve()}"', + ] + if gff_file: + r_code_params.append(f'gffAnnotation="{gff_file.resolve()}"') + if threads > 1: # Rsubread's default is usually 1, so only add if > 1 + r_code_params.append(f'nthreads={threads}') + if memory is not None: + r_code_params.append(f'memory={memory}') + + r_code = f"buildindex({', '.join(r_code_params)})\n" + + result = _run_r_script(r_code, "buildindex") + + # Rsubread creates multiple index files with the given prefix. + # We check for one common file to confirm creation. + expected_index_file_part = Path(f"{index_name.resolve()}.00.b.array") + if expected_index_file_part.is_file(): + # Return the prefix as a representation of the generated index files + result["output_files"].append(str(index_name.resolve())) + + return result + + +@mcp.tool() +def align_reads( + index_name: Path, + read_files: List[Path], + output_bam: Path, + paired_end: bool = False, + threads: int = 1, + unique: bool = False, + max_mismatches: int = 3, + n_subreads: Optional[int] = None, + output_format: str = "BAM", + sort_by_read_name: bool = False, + sort_by_coordinate: bool = False, + phred_offset: int = 33, +) -> dict: + """ + Aligns sequencing reads to a reference genome using the Subread aligner. + + Args: + index_name: Path to the Subread index prefix (created by build_index). + read_files: List of input FASTQ or BAM files. For paired-end, provide two files. + output_bam: Path for the output alignment BAM/SAM file. + paired_end: Set to True if reads are paired-end. + threads: Number of CPU threads to use. Must be at least 1. + unique: If True, only uniquely mapped reads are reported. + max_mismatches: Maximum number of mismatches allowed for an alignment. Must be non-negative. + n_subreads: Number of subreads to use for long reads (if applicable). Must be a positive integer if provided. + output_format: Output format for alignments ("BAM" or "SAM"). + sort_by_read_name: If True, sort output by read name. Cannot be used with `sort_by_coordinate`. + sort_by_coordinate: If True, sort output by coordinate. Cannot be used with `sort_by_read_name`. + phred_offset: Phred score offset (33 or 64). + + Returns: + A dictionary containing command execution details, stdout, stderr, and output files. + """ + # Input validation + if not Path(f"{index_name.resolve()}.00.b.array").is_file(): # Check for a common index file + raise ValueError(f"Subread index not found at prefix: {index_name}") + for f in read_files: + if not f.is_file(): + raise ValueError(f"Read file not found: {f}") + if not read_files: + raise ValueError("At least one read file must be provided.") + if paired_end and len(read_files) != 2: + raise ValueError("For paired-end alignment, exactly two read files must be provided.") + if not paired_end and len(read_files) != 1: + raise ValueError("For single-end alignment, exactly one read file must be provided.") + if threads < 1: + raise ValueError("Number of threads must be at least 1.") + if max_mismatches < 0: + raise ValueError("Maximum mismatches cannot be negative.") + if n_subreads is not None and n_subreads <= 0: + raise ValueError("Number of subreads must be a positive integer.") + if output_format not in ["BAM", "SAM"]: + raise ValueError("Output format must be 'BAM' or 'SAM'.") + if phred_offset not in [33, 64]: + raise ValueError("Phred offset must be 33 or 64.") + if sort_by_read_name and sort_by_coordinate: + raise ValueError("Cannot sort by both read name and coordinate simultaneously.") + + read_files_str = ', '.join([f'"{f.resolve()}"' for f in read_files]) + + r_code_params = [ + f'index="{index_name.resolve()}"', + f'readfile=c({read_files_str})', + f'output_file="{output_bam.resolve()}"', + f'nthreads={threads}', + f'unique={str(unique).upper()}', + f'maxMismatches={max_mismatches}', + f'type="{output_format}"', + f'phredOffset={phred_offset}', + ] + if paired_end: + r_code_params.append(f'pairedEnd={str(paired_end).upper()}') + if n_subreads is not None: + r_code_params.append(f'nsubreads={n_subreads}') + if sort_by_read_name: + r_code_params.append(f'sortByReadName={str(sort_by_read_name).upper()}') + if sort_by_coordinate: + r_code_params.append(f'sortReadsByCoordinate={str(sort_by_coordinate).upper()}') + + r_code = f"align({', '.join(r_code_params)})\n" + + result = _run_r_script(r_code, "align") + + if output_bam.is_file(): + result["output_files"].append(str(output_bam.resolve())) + + return result + + +@mcp.tool() +def count_features( + bam_files: List[Path], + annotation_file: Path, + output_file: Path, + feature_type: str = "exon", + gene_id_attribute: str = "gene_id", + is_paired_end: bool = False, + strand_specific: int = 0, + allow_multi_overlap: bool = False, + min_overlap: int = 1, + ignore_multimapping: bool = False, + n_threads: int = 1, + count_multi_mapping_reads: bool = False, + count_primary_alignments_only: bool = True, + fraction: bool = False, + is_long_read: bool = False, +) -> dict: + """ + Counts reads for genomic features (e.g., genes, exons) using featureCounts. + + Args: + bam_files: List of input BAM/SAM files. + annotation_file: Path to the GTF/GFF annotation file. + output_file: Path for the output count file (e.g., TSV). + feature_type: Feature type to count (e.g., "exon", "gene"). + gene_id_attribute: Attribute in the annotation file to use as gene ID (e.g., "gene_id"). + is_paired_end: Set to True if reads are paired-end. + strand_specific: Strand specificity (0: unstranded, 1: stranded, 2: reversely stranded). + allow_multi_overlap: If True, allow reads to overlap multiple features. + min_overlap: Minimum number of overlapping bases required. Must be at least 1. + ignore_multimapping: If True, ignore multi-mapping reads. + n_threads: Number of CPU threads to use. Must be at least 1. + count_multi_mapping_reads: If True, count multi-mapping reads. + count_primary_alignments_only: If True, count only primary alignments. + fraction: If True, assign fractional counts to multi-overlapping features. + is_long_read: If True, treat reads as long reads. + + Returns: + A dictionary containing command execution details, stdout, stderr, and output files. + """ + # Input validation + for f in bam_files: + if not f.is_file(): + raise ValueError(f"BAM/SAM file not found: {f}") + if not annotation_file.is_file(): + raise ValueError(f"Annotation file not found: {annotation_file}") + if not bam_files: + raise ValueError("At least one BAM/SAM file must be provided.") + if strand_specific not in [0, 1, 2]: + raise ValueError("Strand specificity must be 0 (unstranded), 1 (stranded), or 2 (reversely stranded).") + if min_overlap < 1: + raise ValueError("Minimum overlap must be at least 1.") + if n_threads < 1: + raise ValueError("Number of threads must be at least 1.") + + bam_files_str = ', '.join([f'"{f.resolve()}"' for f in bam_files]) + + # Infer isGTFAnnotationFile based on suffix + is_gtf = annotation_file.suffix.lower() in [".gtf", ".gff"] + + r_code_params = [ + f'files=c({bam_files_str})', + f'annot.ext="{annotation_file.resolve()}"', + f'isGTFAnnotationFile={str(is_gtf).upper()}', + f'featureType="{feature_type}"', + f'attrType="{gene_id_attribute}"', + f'isPairedEnd={str(is_paired_end).upper()}', + f'strandSpecific={strand_specific}', + f'allowMultiOverlap={str(allow_multi_overlap).upper()}', + f'minOverlap={min_overlap}', + f'ignoreMultimapping={str(ignore_multimapping).upper()}', + f'nthreads={n_threads}', + f'countMultiMappingReads={str(count_multi_mapping_reads).upper()}', + f'countPrimaryAlignmentsOnly={str(count_primary_alignments_only).upper()}', + f'fraction={str(fraction).upper()}', + f'isLongRead={str(is_long_read).upper()}', + ] + + # featureCounts returns a list, we need to extract the counts table and write it. + # The counts table is typically in the 'counts' element of the returned list, + # and summary statistics in 'stat'. + summary_output_file = Path(f"{output_file.resolve()}.summary") + r_code = ( + f"fc_result <- featureCounts({', '.join(r_code_params)})\n" + f'write.table(fc_result$counts, file="{output_file.resolve()}", sep="\\t", quote=FALSE, col.names=NA)\n' + f'write.table(fc_result$stat, file="{summary_output_file.resolve()}", sep="\\t", quote=FALSE, row.names=FALSE)\n' + ) + + result = _run_r_script(r_code, "featureCounts") + + if output_file.is_file(): + result["output_files"].append(str(output_file.resolve())) + if summary_output_file.is_file(): + result["output_files"].append(str(summary_output_file.resolve())) + + return result + + +@mcp.tool() +def detect_structural_variants( + bam_files: List[Path], + reference_fasta: Path, + output_vcf: Path, + threads: int = 1, + min_read_support: int = 3, + min_sv_size: int = 50, + max_sv_size: int = 1000000, + min_map_quality: int = 20, +) -> dict: + """ + Detects structural variants (SVs) from BAM files using Rsubread's detectSV. + + Args: + bam_files: List of input BAM/SAM files. + reference_fasta: Path to the reference genome FASTA file. + output_vcf: Path for the output VCF file containing detected SVs. + threads: Number of CPU threads to use. Must be at least 1. + min_read_support: Minimum number of reads supporting an SV to be reported. Must be at least 1. + min_sv_size: Minimum size of SV to be reported. Must be at least 1. + max_sv_size: Maximum size of SV to be reported. Must be greater than or equal to `min_sv_size`. + min_map_quality: Minimum mapping quality of reads to be considered. Must be non-negative. + + Returns: + A dictionary containing command execution details, stdout, stderr, and output files. + """ + # Input validation + for f in bam_files: + if not f.is_file(): + raise ValueError(f"BAM/SAM file not found: {f}") + if not reference_fasta.is_file(): + raise ValueError(f"Reference FASTA file not found: {reference_fasta}") + if not bam_files: + raise ValueError("At least one BAM/SAM file must be provided.") + if threads < 1: + raise ValueError("Number of threads must be at least 1.") + if min_read_support < 1: + raise ValueError("Minimum read support must be at least 1.") + if min_sv_size < 1: + raise ValueError("Minimum SV size must be at least 1.") + if max_sv_size < min_sv_size: + raise ValueError("Maximum SV size cannot be less than minimum SV size.") + if min_map_quality < 0: + raise ValueError("Minimum mapping quality cannot be negative.") + + bam_files_str = ', '.join([f'"{f.resolve()}"' for f in bam_files]) + + r_code_params = [ + f'bamFile=c({bam_files_str})', + f'reference="{reference_fasta.resolve()}"', + f'outputFile="{output_vcf.resolve()}"', + f'nthreads={threads}', + f'minReadSupport={min_read_support}', + f'minSVSize={min_sv_size}', + f'maxSVSize={max_sv_size}', + f'minMapQual={min_map_quality}', + ] + + r_code = f"detectSV({', '.join(r_code_params)})\n" + + result = _run_r_script(r_code, "detectSV") + + if output_vcf.is_file(): + result["output_files"].append(str(output_vcf.resolve())) + + return result + + +@mcp.tool() +def find_gene_fusions( + bam_files: List[Path], + reference_fasta: Path, + output_file: Path, + threads: int = 1, + min_read_support: int = 3, + min_junction_reads: int = 1, +) -> dict: + """ + Detects gene fusions from BAM files using Rsubread's findFusion. + + Args: + bam_files: List of input BAM/SAM files. + reference_fasta: Path to the reference genome FASTA file. + output_file: Path for the output file containing detected gene fusions (e.g., TSV). + threads: Number of CPU threads to use. Must be at least 1. + min_read_support: Minimum number of reads supporting a fusion to be reported. Must be at least 1. + min_junction_reads: Minimum number of reads spanning the fusion junction. Must be at least 1. + + Returns: + A dictionary containing command execution details, stdout, stderr, and output files. + """ + # Input validation + for f in bam_files: + if not f.is_file(): + raise ValueError(f"BAM/SAM file not found: {f}") + if not reference_fasta.is_file(): + raise ValueError(f"Reference FASTA file not found: {reference_fasta}") + if not bam_files: + raise ValueError("At least one BAM/SAM file must be provided.") + if threads < 1: + raise ValueError("Number of threads must be at least 1.") + if min_read_support < 1: + raise ValueError("Minimum read support must be at least 1.") + if min_junction_reads < 1: + raise ValueError("Minimum junction reads must be at least 1.") + + bam_files_str = ', '.join([f'"{f.resolve()}"' for f in bam_files]) + + r_code_params = [ + f'bamFile=c({bam_files_str})', + f'reference="{reference_fasta.resolve()}"', + f'outputFile="{output_file.resolve()}"', + f'nthreads={threads}', + f'minReadSupport={min_read_support}', + f'minJunctionReads={min_junction_reads}', + ] + + r_code = f"findFusion({', '.join(r_code_params)})\n" + + result = _run_r_script(r_code, "findFusion") + + if output_file.is_file(): + result["output_files"].append(str(output_file.resolve())) + + return result \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-rsubread/app/bioconductor-rsubread_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-rsubread/app/bioconductor-rsubread_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..f61b643bfbae0660e35563150014a240ef6193aa --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-rsubread/app/bioconductor-rsubread_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-rsubread/app/bioconductor-rsubread_server.py') +SERVER_NAME = 'biosci_bioconductor_rsubread' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-rsubread/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-rsubread/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-rsubread/app/requirements.txt @@ -0,0 +1 @@ + diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-rsubread/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-rsubread/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..e2725ed890f216390d4ca45f72fe80e813511c82 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-rsubread/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-bioconductor-rsubread: + build: . + image: mcp-bioconductor-rsubread:latest + container_name: mcp-bioconductor-rsubread + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=bioconductor-rsubread + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-rsubread/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-rsubread/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..fdd1061efad1bac463df38b14d847bf8a1798168 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-rsubread/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - bioconductor-rsubread + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-rsubread/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-rsubread/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-rsubread/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-rtracklayer/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-rtracklayer/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-rtracklayer/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-scarray.sat/app/bioconductor-scarray.sat_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-scarray.sat/app/bioconductor-scarray.sat_server.py new file mode 100644 index 0000000000000000000000000000000000000000..9929f15ac3d64e02e0ab8a480ab77af92f5030ac --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-scarray.sat/app/bioconductor-scarray.sat_server.py @@ -0,0 +1,138 @@ +import subprocess +import tempfile +from pathlib import Path +from typing import Dict, Optional + +# MCP decorator is assumed to be available from the environment +# import mcp + +@mcp.tool() +def create_gds_seurat_object( + input_dir: Path, + output_rds: Path, + output_gds: Path, + project_name: str = "scproject", + min_cells: int = 3, + min_features: int = 200, +) -> Dict: + """ + Creates a GDS-backed Seurat object from 10x Genomics data using SCArray.sat. + + This tool wraps the bioconductor-scarray.sat R package to convert a standard + 10x Genomics count matrix directory (containing matrix.mtx, features.tsv, + and barcodes.tsv files) into a Seurat object. The key feature is that the + count matrix is stored in a memory-efficient Genomic Data Structure (GDS) file, + making it suitable for very large datasets. + + Args: + input_dir: Path to the input directory containing 10x Genomics data files + (e.g., 'matrix.mtx.gz', 'features.tsv.gz', 'barcodes.tsv.gz'). + output_rds: Path to save the output Seurat object (.rds file). + output_gds: Path to save the generated Genomic Data Structure (.gds file). + project_name: Name for the Seurat project. + min_cells: Include features detected in at least this many cells. + min_features: Include cells with at least this many detected features. + + Returns: + A dictionary containing the executed command, stdout, stderr, and a + dictionary of output file paths. + """ + # --- Input Validation --- + if not input_dir.is_dir(): + raise ValueError(f"Input directory '{input_dir}' does not exist or is not a directory.") + + # Check for required 10x files (allowing for .gz extension and alternative names) + barcodes_path = next(input_dir.glob("barcodes.tsv*"), None) + features_path = next(input_dir.glob("features.tsv*"), None) or next(input_dir.glob("genes.tsv*"), None) + matrix_path = next(input_dir.glob("matrix.mtx*"), None) + + if not all([barcodes_path, features_path, matrix_path]): + raise FileNotFoundError( + "The input directory must contain 'barcodes.tsv', 'features.tsv' (or 'genes.tsv'), " + "and 'matrix.mtx'. Compressed versions (.gz) are accepted." + ) + + if min_cells < 0: + raise ValueError("min_cells must be a non-negative integer.") + if min_features < 0: + raise ValueError("min_features must be a non-negative integer.") + + # --- R Script Generation --- + # This script performs the core logic using the R libraries. + r_script_content = f""" + # Load required libraries + library(Seurat) + library(SCArray.sat) + library(SCArray) + + # Define parameters passed from the Python wrapper + input_dir_path <- "{input_dir.resolve()}" + output_rds_path <- "{output_rds.resolve()}" + output_gds_path <- "{output_gds.resolve()}" + project_id <- "{project_name}" + min_cell_count <- {min_cells} + min_feature_count <- {min_features} + + # Read the 10X data. Read10X handles compressed files automatically. + counts <- Seurat::Read10X(data.dir = input_dir_path) + + # Create the GDS file from the count matrix. This is the main step + # provided by the SCArray package. + SCArray::scarray_create(output_gds_path, counts, verbose = TRUE) + + # Create the Seurat object using the GDS-backed matrix. + # The scarray() function loads the GDS file as a DelayedMatrix object. + seurat_obj <- Seurat::CreateSeuratObject( + counts = SCArray::scarray(output_gds_path), + project = project_id, + min.cells = min_cell_count, + min.features = min_feature_count + ) + + # Save the final Seurat object to an RDS file for later use in R. + saveRDS(seurat_obj, file = output_rds_path) + + cat("Successfully created GDS-backed Seurat object and saved to RDS.\\n") + """ + + # --- Subprocess Execution --- + # Use a temporary file to hold the R script + r_script_path = None + try: + with tempfile.NamedTemporaryFile(mode='w', suffix=".R", delete=False) as r_script_file: + r_script_file.write(r_script_content) + r_script_path = Path(r_script_file.name) + + cmd = ["Rscript", str(r_script_path)] + command_executed = " ".join(cmd) + + result = subprocess.run( + cmd, + check=True, + capture_output=True, + text=True, + ) + + # --- Return Structured Output --- + return { + "command_executed": command_executed, + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": { + "seurat_object_rds": str(output_rds.resolve()), + "gds_file": str(output_gds.resolve()) + } + } + except subprocess.CalledProcessError as e: + # --- Error Handling --- + return { + "command_executed": command_executed, + "stdout": e.stdout, + "stderr": e.stderr, + "return_code": e.returncode, + "error": "R script execution failed. Please check the logs for details from Seurat or SCArray." + } + finally: + # Ensure the temporary R script is cleaned up + if r_script_path and r_script_path.exists(): + r_script_path.unlink() diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-scarray.sat/app/bioconductor-scarray.sat_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-scarray.sat/app/bioconductor-scarray.sat_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..c2ec698164f847a41c4d28c009bd64e6581fc2d9 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-scarray.sat/app/bioconductor-scarray.sat_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-scarray.sat/app/bioconductor-scarray.sat_server.py') +SERVER_NAME = 'biosci_bioconductor_scarray_sat' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-scarray.sat/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-scarray.sat/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..db5d8105ceb8dd08e531d26a061c149becd2a8cb --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-scarray.sat/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-bioconductor-scarray.sat: + build: . + image: mcp-bioconductor-scarray.sat:latest + container_name: mcp-bioconductor-scarray.sat + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=bioconductor-scarray.sat + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-scarray.sat/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-scarray.sat/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..af4e00630553847553f47ff1414b313f83f1b409 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-scarray.sat/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - bioconductor-scarray.sat + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-scarray.sat/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-scarray.sat/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-scarray.sat/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-scdotplot/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-scdotplot/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..3d4c868a815aa829265425f503d9268db6cc690e --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-scdotplot/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install bioconductor-scdotplot via conda (e.g., from bioconda) +RUN conda install -c bioconda bioconductor-scdotplot -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY app/bioconductor-scdotplot_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/bioconductor-scdotplot_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/bioconductor-scdotplot_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-scdotplot/app/bioconductor-scdotplot_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-scdotplot/app/bioconductor-scdotplot_server.py new file mode 100644 index 0000000000000000000000000000000000000000..d4fce23c3ba0d1aebe5c1a8c1847921e95095ff8 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-scdotplot/app/bioconductor-scdotplot_server.py @@ -0,0 +1,265 @@ +import subprocess +import tempfile +import json +from pathlib import Path +from typing import List, Optional, Union + +@mcp.tool() +def sc_dot_plot( + input_path: str, + features: List[str], + group_by: str, + output_path: str = "dotplot.pdf", + cluster_cols: bool = True, + cluster_rows: bool = True, + assay: str = "RNA", + slot: str = "data", + scale: bool = True, + cap: float = 2.5, + annotation_cols: Optional[List[str]] = None, + annotation_rows: Optional[List[str]] = None, +) -> dict: + """ + Generate a clustered single-cell RNA-seq dot plot using the scDotPlot package. + + Args: + input_path: Path to the input file (RDS format containing Seurat or SingleCellExperiment object). + features: List of gene names or features to plot. + group_by: Metadata column name to group cells by (e.g., 'ident' or 'cluster'). + output_path: Path where the resulting plot (PDF, PNG, etc.) will be saved. + cluster_cols: Whether to perform hierarchical clustering on the columns (features). + cluster_rows: Whether to perform hierarchical clustering on the rows (cell groups). + assay: Name of the assay to use (for Seurat objects). + slot: Name of the slot to use (e.g., 'data', 'counts'). + scale: Whether to scale the expression data. + cap: Value at which to cap the scaled expression data. + annotation_cols: Optional list of metadata columns to use for annotating the columns. + annotation_rows: Optional list of metadata columns to use for annotating the rows. + """ + # Input validation + input_file = Path(input_path) + if not input_file.exists(): + return {"error": f"Input file not found: {input_path}"} + + output_file = Path(output_path) + output_file.parent.mkdir(parents=True, exist_ok=True) + + # Prepare R script + features_r = ", ".join([f"'{f}'" for f in features]) + + # Handle optional annotations + ann_cols_r = "NULL" + if annotation_cols: + ann_cols_r = "c(" + ", ".join([f"'{a}'" for a in annotation_cols]) + ")" + + ann_rows_r = "NULL" + if annotation_rows: + ann_rows_r = "c(" + ", ".join([f"'{a}'" for a in annotation_rows]) + ")" + + r_script = f""" + library(scDotPlot) + library(Seurat) + library(SingleCellExperiment) + library(ggplot2) + + # Load object + obj <- readRDS('{input_path}') + + # Generate plot + p <- scDotPlot( + object = obj, + features = c({features_r}), + group.by = '{group_by}', + cluster.cols = {str(cluster_cols).upper()}, + cluster.rows = {str(cluster_rows).upper()}, + assay = '{assay}', + slot = '{slot}', + scale = {str(scale).upper()}, + cap = {cap}, + annotation_cols = {ann_cols_r}, + annotation_rows = {ann_rows_r} + ) + + # Save plot + ggsave('{output_path}', plot = p) + """ + + with tempfile.NamedTemporaryFile(mode='w', suffix='.R', delete=False) as tmp: + tmp.write(r_script) + tmp_path = tmp.name + + try: + result = subprocess.run( + ["Rscript", tmp_path], + capture_output=True, + text=True, + check=True + ) + return { + "command_executed": f"scDotPlot on {input_path}", + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": [str(output_file.absolute())] + } + except subprocess.CalledProcessError as e: + return { + "error": "R execution failed", + "stdout": e.stdout, + "stderr": e.stderr, + "command_executed": f"Rscript {tmp_path}" + } + finally: + Path(tmp_path).unlink(missing_ok=True) + +@mcp.tool() +def sc_dot_plot_data( + input_path: str, + features: List[str], + group_by: str, + output_csv: str = "dotplot_data.csv", + assay: str = "RNA", + slot: str = "data", + scale: bool = True, + cap: float = 2.5, +) -> dict: + """ + Extract the underlying data frame used for generating a scDotPlot. + + Args: + input_path: Path to the input file (RDS format). + features: List of gene names or features. + group_by: Metadata column name to group cells by. + output_csv: Path to save the extracted data as a CSV file. + assay: Name of the assay to use. + slot: Name of the slot to use. + scale: Whether to scale the expression data. + cap: Value at which to cap the scaled expression data. + """ + input_file = Path(input_path) + if not input_file.exists(): + return {"error": f"Input file not found: {input_path}"} + + features_r = ", ".join([f"'{f}'" for f in features]) + + r_script = f""" + library(scDotPlot) + library(Seurat) + library(SingleCellExperiment) + + obj <- readRDS('{input_path}') + + # Extract data using the internal data preparation logic + plot_data <- scDotPlotData( + object = obj, + features = c({features_r}), + group.by = '{group_by}', + assay = '{assay}', + slot = '{slot}', + scale = {str(scale).upper()}, + cap = {cap} + ) + + write.csv(plot_data, '{output_csv}', row.names = FALSE) + """ + + with tempfile.NamedTemporaryFile(mode='w', suffix='.R', delete=False) as tmp: + tmp.write(r_script) + tmp_path = tmp.name + + try: + result = subprocess.run( + ["Rscript", tmp_path], + capture_output=True, + text=True, + check=True + ) + return { + "command_executed": f"scDotPlotData on {input_path}", + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": [str(Path(output_csv).absolute())] + } + except subprocess.CalledProcessError as e: + return { + "error": "R execution failed", + "stdout": e.stdout, + "stderr": e.stderr + } + finally: + Path(tmp_path).unlink(missing_ok=True) + +@mcp.tool() +def sc_dot_plot_custom_df( + data_path: str, + features_col: str, + groups_col: str, + avg_exp_col: str, + pct_exp_col: str, + output_path: str = "dotplot_from_df.pdf", + cluster_cols: bool = True, + cluster_rows: bool = True, +) -> dict: + """ + Generate a clustered dot plot from a pre-calculated data frame instead of a Seurat/SCE object. + + Args: + data_path: Path to a CSV file containing the summarized expression data. + features_col: Column name in the CSV containing gene/feature names. + groups_col: Column name in the CSV containing cell group/cluster names. + avg_exp_col: Column name in the CSV containing average expression values. + pct_exp_col: Column name in the CSV containing percent of cells expressing the gene. + output_path: Path where the resulting plot will be saved. + cluster_cols: Whether to perform hierarchical clustering on columns. + cluster_rows: Whether to perform hierarchical clustering on rows. + """ + data_file = Path(data_path) + if not data_file.exists(): + return {"error": f"Data file not found: {data_path}"} + + r_script = f""" + library(scDotPlot) + library(ggplot2) + + # Load data frame + df <- read.csv('{data_path}') + + # Generate plot using the data frame method + # Note: scDotPlot handles data frames by mapping columns + p <- scDotPlot( + object = df, + features = '{features_col}', + group.by = '{groups_col}', + avg.exp = '{avg_exp_col}', + pct.exp = '{pct_exp_col}', + cluster.cols = {str(cluster_cols).upper()}, + cluster.rows = {str(cluster_rows).upper()} + ) + + ggsave('{output_path}', plot = p) + """ + + with tempfile.NamedTemporaryFile(mode='w', suffix='.R', delete=False) as tmp: + tmp.write(r_script) + tmp_path = tmp.name + + try: + result = subprocess.run( + ["Rscript", tmp_path], + capture_output=True, + text=True, + check=True + ) + return { + "command_executed": f"scDotPlot on data frame {data_path}", + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": [str(Path(output_path).absolute())] + } + except subprocess.CalledProcessError as e: + return { + "error": "R execution failed", + "stdout": e.stdout, + "stderr": e.stderr + } + finally: + Path(tmp_path).unlink(missing_ok=True) \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-scdotplot/app/bioconductor-scdotplot_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-scdotplot/app/bioconductor-scdotplot_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..4cf11410a2198475428a5e4e13de21ca25accd6c --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-scdotplot/app/bioconductor-scdotplot_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-scdotplot/app/bioconductor-scdotplot_server.py') +SERVER_NAME = 'biosci_bioconductor_scdotplot' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-scdotplot/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-scdotplot/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-scdotplot/app/requirements.txt @@ -0,0 +1 @@ + diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-scdotplot/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-scdotplot/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..75f17138ca6bc36ee85fef8d3f0095d8611fcb9c --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-scdotplot/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-bioconductor-scdotplot: + build: . + image: mcp-bioconductor-scdotplot:latest + container_name: mcp-bioconductor-scdotplot + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=bioconductor-scdotplot + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-scdotplot/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-scdotplot/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..a13eaadf703def4a3951f4ee0662665909ca7c19 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-scdotplot/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - bioconductor-scdotplot + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-scdotplot/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-scdotplot/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-scdotplot/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-sctreeviz/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-sctreeviz/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..db3573a50eaad1021ad649a63e1b97a92c25f462 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-sctreeviz/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install bioconductor-sctreeviz via conda (e.g., from bioconda) +RUN conda install -c bioconda bioconductor-sctreeviz -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY app/bioconductor-sctreeviz_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/bioconductor-sctreeviz_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/bioconductor-sctreeviz_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-sctreeviz/app/bioconductor-sctreeviz_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-sctreeviz/app/bioconductor-sctreeviz_server.py new file mode 100644 index 0000000000000000000000000000000000000000..0a4e645265bc22e4ddb59df7a39b0c26e216fe82 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-sctreeviz/app/bioconductor-sctreeviz_server.py @@ -0,0 +1,222 @@ +import subprocess +import tempfile +from pathlib import Path +from typing import List, Optional, Dict, Any +import logging +import shlex + +# Set up logging +logging.basicConfig(level=logging.INFO) +logger = logging.getLogger(__name__) + +# Mock mcp.tool decorator for standalone execution +def mcp_tool_mock(): + def decorator(func): + def wrapper(*args, **kwargs): + return func(*args, **kwargs) + return wrapper + return decorator + +mcp = type('mcp', (), {'tool': mcp_tool_mock})() + + +@mcp.tool() +def generate_cluster_tree( + sce_rds: Path, + output_plot: Path, + cluster_steps: str = "2,4,6,8", + assay_name: str = "logcounts", + dimred_name: str = "PCA", + cluster_prefix: str = "sctv_cluster_", +) -> Dict[str, Any]: + """ + Generates a cluster tree visualization from a SingleCellExperiment object. + + This tool uses the scTreeViz R package to perform clustering at multiple + resolutions and visualize the resulting hierarchy as a 'clustree' plot. + It requires a pre-processed SingleCellExperiment object saved as an .rds file, + which must contain a dimensionality reduction result (e.g., PCA). + + Args: + sce_rds: Path to the input SingleCellExperiment object saved as an RDS file. + This object must contain a dimensionality reduction result specified + by `dimred_name`. + output_plot: Path to save the output plot. The file extension (e.g., .png, + .pdf, .svg) determines the output format. + cluster_steps: A comma-separated string of integer 'steps' for the + `igraph::cluster_walktrap` algorithm, used to generate + clusters at different resolutions. + assay_name: The name of the assay in the SCE object to use for building the graph. + dimred_name: The name of the dimensionality reduction result in the SCE + object to use for building the SNN graph. + cluster_prefix: The prefix for the new cluster columns added to the + colData of the SCE object. + + Returns: + A dictionary containing the executed command, stdout, stderr, and a list + of output files. + """ + # 1. Input validation + if not sce_rds.is_file(): + raise FileNotFoundError(f"Input file not found: {sce_rds}") + + if not output_plot.parent.exists(): + logger.info(f"Output directory {output_plot.parent} does not exist. Creating it.") + output_plot.parent.mkdir(parents=True, exist_ok=True) + + try: + # Validate that cluster_steps contains comma-separated integers + [int(s.strip()) for s in cluster_steps.split(',')] + except ValueError: + raise ValueError("`cluster_steps` must be a comma-separated string of integers.") + + # 2. R script generation + r_script_content = f""" + # Load necessary libraries + if (!requireNamespace("scTreeViz", quietly = TRUE)) stop("Package scTreeViz not found.") + if (!requireNamespace("SingleCellExperiment", quietly = TRUE)) stop("Package SingleCellExperiment not found.") + if (!requireNamespace("scran", quietly = TRUE)) stop("Package scran not found.") + if (!requireNamespace("igraph", quietly = TRUE)) stop("Package igraph not found.") + if (!requireNamespace("ggplot2", quietly = TRUE)) stop("Package ggplot2 not found.") + + library(scTreeViz) + library(SingleCellExperiment) + library(scran) + library(igraph) + library(ggplot2) + + # --- Get arguments from command line --- + args <- commandArgs(trailingOnly=TRUE) + sce_rds_path <- args[1] + output_plot_path <- args[2] + cluster_steps_str <- args[3] + assay_name <- args[4] + dimred_name <- args[5] + cluster_prefix <- args[6] + + cat("--- scTreeViz Analysis Parameters ---\\n") + cat("Input SCE RDS: ", sce_rds_path, "\\n") + cat("Output Plot: ", output_plot_path, "\\n") + cat("Cluster Steps: ", cluster_steps_str, "\\n") + cat("Assay Name: ", assay_name, "\\n") + cat("DimRed Name: ", dimred_name, "\\n") + cat("Cluster Prefix: ", cluster_prefix, "\\n") + cat("-------------------------------------\\n\\n") + + # --- Main analysis --- + tryCatch({{ + # Read input SingleCellExperiment object + cat("Reading SCE object from:", sce_rds_path, "\\n") + sce <- readRDS(sce_rds_path) + + # Check for dimensionality reduction result + if (!dimred_name %in% reducedDimNames(sce)) {{ + stop(paste0("Dimensionality reduction '", dimred_name, "' not found in the SCE object.")) + }} + + # Build SNN graph + cat("Building SNN graph using '", dimred_name, "'...\\n") + g <- buildSNNGraph(sce, use.dimred=dimred_name, assay.type=assay_name) + + # Parse cluster steps + steps_vec <- as.integer(strsplit(cluster_steps_str, ",")[[1]]) + + # Perform clustering for each step value + cat("Performing clustering for steps:", paste(steps_vec, collapse=", "), "\\n") + for (s in steps_vec) {{ + set.seed(42) # for reproducibility + col_name <- paste0(cluster_prefix, "s", s) + clust <- igraph::cluster_walktrap(g, steps=s)$membership + sce[[col_name]] <- as.factor(clust) + }} + + # Get the names of the cluster columns we just created + clust_cols <- grep(paste0("^", cluster_prefix), colnames(colData(sce)), value = TRUE) + if (length(clust_cols) == 0) {{ + stop("No cluster columns were generated. Check cluster_prefix and clustering steps.") + }} + cat("Generated cluster columns:", paste(clust_cols, collapse=", "), "\\n") + + # Create the TreeViz object + cat("Creating TreeViz object...\\n") + sctv <- TreeViz(sce, colData_cols = clust_cols) + + # Generate the clustree plot + cat("Generating clustree plot...\\n") + p <- sctv_clustree(sctv, prefix = cluster_prefix) + + # Save the plot + cat("Saving plot to:", output_plot_path, "\\n") + ggsave(output_plot_path, plot = p, width = 10, height = 8, dpi = 300) + + cat("\\n--- Analysis successfully completed! ---\\n") + + }}, error = function(e) {{ + cat("Error during scTreeViz analysis:\\n") + message(e) + quit(status = 1) + }}) + """ + + # 3. Subprocess execution + command = [] + result_dict = { + "command_executed": "", + "stdout": "", + "stderr": "", + "output_files": [], + } + + try: + with tempfile.NamedTemporaryFile(mode='w', delete=False, suffix=".R") as r_script_file: + r_script_file.write(r_script_content) + r_script_path = r_script_file.name + + command = [ + "Rscript", + r_script_path, + str(sce_rds), + str(output_plot), + cluster_steps, + assay_name, + dimred_name, + cluster_prefix, + ] + + command_str = " ".join(shlex.quote(c) for c in command) + logger.info(f"Executing command: {command_str}") + result_dict["command_executed"] = command_str + + process = subprocess.run( + command, + capture_output=True, + text=True, + check=True, + ) + + result_dict["stdout"] = process.stdout + result_dict["stderr"] = process.stderr + if output_plot.exists(): + result_dict["output_files"].append(str(output_plot)) + + logger.info(f"Successfully generated cluster tree plot: {output_plot}") + + except FileNotFoundError: + error_msg = "Rscript not found. Please ensure R is installed and in your PATH." + logger.error(error_msg) + result_dict["stderr"] = error_msg + raise RuntimeError(error_msg) from None + except subprocess.CalledProcessError as e: + logger.error(f"Error executing scTreeViz R script.") + logger.error(f"Return code: {e.returncode}") + logger.error(f"Stdout: {e.stdout}") + logger.error(f"Stderr: {e.stderr}") + result_dict["stdout"] = e.stdout + result_dict["stderr"] = e.stderr + raise + finally: + # Clean up the temporary R script + if 'r_script_path' in locals() and Path(r_script_path).exists(): + Path(r_script_path).unlink() + + return result_dict \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-sctreeviz/app/bioconductor-sctreeviz_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-sctreeviz/app/bioconductor-sctreeviz_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..483e706c6c9ff0537d6f42c24ff831d3cea3659e --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-sctreeviz/app/bioconductor-sctreeviz_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-sctreeviz/app/bioconductor-sctreeviz_server.py') +SERVER_NAME = 'biosci_bioconductor_sctreeviz' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-sctreeviz/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-sctreeviz/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-sctreeviz/app/requirements.txt @@ -0,0 +1 @@ + diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-sctreeviz/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-sctreeviz/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..d2f1a306dafbdf96297b18b6841fa9e72e0aede1 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-sctreeviz/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-bioconductor-sctreeviz: + build: . + image: mcp-bioconductor-sctreeviz:latest + container_name: mcp-bioconductor-sctreeviz + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=bioconductor-sctreeviz + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-sctreeviz/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-sctreeviz/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..15505792d6eebae17f827d895c90a82b765b845e --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-sctreeviz/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - bioconductor-sctreeviz + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-sctreeviz/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-sctreeviz/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-sctreeviz/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-seraster/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-seraster/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..9582030e3afccb39e21c45951a522ff905f10287 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-seraster/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install bioconductor-seraster via conda (e.g., from bioconda) +RUN conda install -c bioconda bioconductor-seraster -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY app/bioconductor-seraster_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/bioconductor-seraster_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/bioconductor-seraster_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-seraster/app/bioconductor-seraster_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-seraster/app/bioconductor-seraster_server.py new file mode 100644 index 0000000000000000000000000000000000000000..0535efae12271c8eb33538252a8f501f30287516 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-seraster/app/bioconductor-seraster_server.py @@ -0,0 +1,206 @@ +import subprocess +import os +from pathlib import Path +from typing import Optional, List, Dict, Any + +@mcp.tool() +def rasterize_gene( + input_rds: str, + output_rds: str, + assay_name: str = "counts", + resolution: float = 100.0, + square: bool = True, + min_cells: int = 0, + n_cores: int = 1 +): + """ + Rasterize gene expression data from a SpatialExperiment object using SEraster. + + Args: + input_rds: Path to the input SpatialExperiment object in RDS format. + output_rds: Path where the rasterized SpatialExperiment object will be saved. + assay_name: Name of the assay to rasterize (e.g., 'counts', 'logcounts'). + resolution: Resolution for rasterization (size of the bins). + square: Whether to use square bins (True) or hexagonal bins (False). + min_cells: Minimum number of cells per bin to retain. + n_cores: Number of cores for parallel processing. + """ + input_path = Path(input_rds) + output_path = Path(output_rds) + + if not input_path.exists(): + return {"error": f"Input file not found: {input_rds}"} + + # Construct R script to execute SEraster::rasterizeGene + r_script = f""" + library(SEraster) + library(SpatialExperiment) + + se <- readRDS('{input_path}') + rasterized_se <- rasterizeGene( + se, + assay_name = '{assay_name}', + resolution = {resolution}, + square = {'TRUE' if square else 'FALSE'}, + min_cells = {min_cells}, + n_cores = {n_cores} + ) + saveRDS(rasterized_se, '{output_path}') + """ + + try: + result = subprocess.run( + ["Rscript", "-e", r_script], + capture_output=True, + text=True, + check=True + ) + return { + "command_executed": f"SEraster::rasterizeGene on {input_rds}", + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": [str(output_path)] + } + except subprocess.CalledProcessError as e: + return { + "error": "R execution failed", + "stdout": e.stdout, + "stderr": e.stderr, + "command_executed": e.cmd + } + +@mcp.tool() +def rasterize_cell_type( + input_rds: str, + output_rds: str, + col_name: str, + resolution: float = 100.0, + square: bool = True, + min_cells: int = 0, + n_cores: int = 1 +): + """ + Rasterize cell type labels or proportions from a SpatialExperiment object using SEraster. + + Args: + input_rds: Path to the input SpatialExperiment object in RDS format. + output_rds: Path where the rasterized SpatialExperiment object will be saved. + col_name: Column name in colData containing cell type labels or proportions. + resolution: Resolution for rasterization (size of the bins). + square: Whether to use square bins (True) or hexagonal bins (False). + min_cells: Minimum number of cells per bin to retain. + n_cores: Number of cores for parallel processing. + """ + input_path = Path(input_rds) + output_path = Path(output_rds) + + if not input_path.exists(): + return {"error": f"Input file not found: {input_rds}"} + + r_script = f""" + library(SEraster) + library(SpatialExperiment) + + se <- readRDS('{input_path}') + rasterized_se <- rasterizeCellType( + se, + col_name = '{col_name}', + resolution = {resolution}, + square = {'TRUE' if square else 'FALSE'}, + min_cells = {min_cells}, + n_cores = {n_cores} + ) + saveRDS(rasterized_se, '{output_path}') + """ + + try: + result = subprocess.run( + ["Rscript", "-e", r_script], + capture_output=True, + text=True, + check=True + ) + return { + "command_executed": f"SEraster::rasterizeCellType on {input_rds}", + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": [str(output_path)] + } + except subprocess.CalledProcessError as e: + return { + "error": "R execution failed", + "stdout": e.stdout, + "stderr": e.stderr, + "command_executed": e.cmd + } + +@mcp.tool() +def seraster_wrapper( + input_rds: str, + output_rds: str, + assay_name: Optional[str] = None, + col_name: Optional[str] = None, + resolution: float = 100.0, + square: bool = True, + min_cells: int = 0, + n_cores: int = 1 +): + """ + Generic wrapper for SEraster to rasterize either genes or cell types based on provided parameters. + + Args: + input_rds: Path to the input SpatialExperiment object in RDS format. + output_rds: Path where the rasterized SpatialExperiment object will be saved. + assay_name: (Optional) Name of the assay to rasterize. + col_name: (Optional) Column name in colData to rasterize. + resolution: Resolution for rasterization. + square: Whether to use square bins (True) or hexagonal bins (False). + min_cells: Minimum number of cells per bin to retain. + n_cores: Number of cores for parallel processing. + """ + input_path = Path(input_rds) + output_path = Path(output_rds) + + if not input_path.exists(): + return {"error": f"Input file not found: {input_rds}"} + + if assay_name is None and col_name is None: + return {"error": "Either assay_name or col_name must be provided."} + + # Determine which function to call in R + if assay_name and not col_name: + r_call = f"rasterizeGene(se, assay_name = '{assay_name}', resolution = {resolution}, square = {'TRUE' if square else 'FALSE'}, min_cells = {min_cells}, n_cores = {n_cores})" + elif col_name and not assay_name: + r_call = f"rasterizeCellType(se, col_name = '{col_name}', resolution = {resolution}, square = {'TRUE' if square else 'FALSE'}, min_cells = {min_cells}, n_cores = {n_cores})" + else: + # If both are provided, SEraster generic function handles it or we default to a specific logic + r_call = f"SEraster(se, assay_name = '{assay_name}', col_name = '{col_name}', resolution = {resolution}, square = {'TRUE' if square else 'FALSE'}, min_cells = {min_cells}, n_cores = {n_cores})" + + r_script = f""" + library(SEraster) + library(SpatialExperiment) + se <- readRDS('{input_path}') + rasterized_se <- {r_call} + saveRDS(rasterized_se, '{output_path}') + """ + + try: + result = subprocess.run( + ["Rscript", "-e", r_script], + capture_output=True, + text=True, + check=True + ) + return { + "command_executed": f"SEraster wrapper call on {input_rds}", + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": [str(output_path)] + } + except subprocess.CalledProcessError as e: + return { + "error": "R execution failed", + "stdout": e.stdout, + "stderr": e.stderr, + "command_executed": e.cmd + } \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-seraster/app/bioconductor-seraster_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-seraster/app/bioconductor-seraster_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..59c24295e07a4568ab360467af131b04effc4ae3 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-seraster/app/bioconductor-seraster_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-seraster/app/bioconductor-seraster_server.py') +SERVER_NAME = 'biosci_bioconductor_seraster' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-seraster/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-seraster/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-seraster/app/requirements.txt @@ -0,0 +1 @@ + diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-seraster/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-seraster/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..534187149cd3343c68e775bab4d9c564e26ff3b3 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-seraster/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-bioconductor-seraster: + build: . + image: mcp-bioconductor-seraster:latest + container_name: mcp-bioconductor-seraster + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=bioconductor-seraster + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-seraster/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-seraster/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..7fa3c4adf558d55a01c062934774fb43e1ec000a --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-seraster/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - bioconductor-seraster + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-seraster/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-seraster/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-seraster/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-spari/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-spari/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..3ba93eb1862824f3260aa923fe403218a5ddfbd9 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-spari/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install bioconductor-spari via conda (e.g., from bioconda) +RUN conda install -c bioconda bioconductor-spari -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY app/bioconductor-spari_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/bioconductor-spari_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/bioconductor-spari_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-spari/app/bioconductor-spari_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-spari/app/bioconductor-spari_server.py new file mode 100644 index 0000000000000000000000000000000000000000..7a9413fcfce8987434a018b247154390a7883edb --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-spari/app/bioconductor-spari_server.py @@ -0,0 +1,261 @@ +import subprocess +import tempfile +import shutil +from pathlib import Path +from typing import Optional, List, Dict, Any + +# Assume 'spari' is an executable available in the system's PATH. +# This wrapper simulates a command-line interface for the bioconductor-spari package, +# as if it were compiled into a standalone tool with subcommands. + +@mcp.tool() +def preprocess_spatial_data( + input_dir: Path, + output_se_object: Path, + min_cells: int = 3, + min_features: int = 200, + normalization_method: str = "LogNormalize", +) -> Dict[str, Any]: + """ + Preprocesses raw spatial transcriptomics data into a format suitable for SPARi analysis. + + This function simulates the 'spari preprocess' command, taking raw spatial data + (e.g., from 10x Genomics Visium) and generating a processed Seurat or + SpatialExperiment object (saved as an RDS file). + + Args: + input_dir: Path to the directory containing raw spatial data (e.g., filtered_feature_bc_matrix, spatial). + output_se_object: Path to save the processed Seurat/SpatialExperiment object as an RDS file. + min_cells: Minimum number of cells a feature must be detected in to be kept. + min_features: Minimum number of features detected in a cell to be kept. + normalization_method: Normalization method to apply (e.g., "LogNormalize", "SCTransform"). + + Returns: + A dictionary containing the command executed, stdout, stderr, and output files. + """ + # Input validation + if not input_dir.is_dir(): + raise ValueError(f"Input directory '{input_dir}' does not exist or is not a directory.") + if output_se_object.suffix != ".rds": + raise ValueError(f"Output file '{output_se_object}' must have a '.rds' extension.") + if min_cells < 0: + raise ValueError("min_cells must be a non-negative integer.") + if min_features < 0: + raise ValueError("min_features must be a non-negative integer.") + if normalization_method not in ["LogNormalize", "SCTransform", "none"]: + raise ValueError(f"Unsupported normalization_method: '{normalization_method}'. " + "Choose from 'LogNormalize', 'SCTransform', or 'none'.") + + # Ensure output directory exists + output_se_object.parent.mkdir(parents=True, exist_ok=True) + + command = [ + "spari", "preprocess", + "--input", str(input_dir), + "--output", str(output_se_object), + "--min-cells", str(min_cells), + "--min-features", str(min_features), + "--normalization", normalization_method, + ] + + try: + process = subprocess.run(command, capture_output=True, text=True, check=True) + stdout = process.stdout + stderr = process.stderr + except FileNotFoundError: + raise RuntimeError("The 'spari' executable was not found. Please ensure it is installed and in your PATH.") + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(command), + "stdout": e.stdout, + "stderr": e.stderr, + "error": f"SPARi preprocess failed with exit code {e.returncode}", + "output_files": [], + } + + return { + "command_executed": " ".join(command), + "stdout": stdout, + "stderr": stderr, + "output_files": [str(output_se_object)], + } + + +@mcp.tool() +def analyze_spatial_data( + se_object_path: Path, + image_features_path: Path, + output_dir: Path, + n_pcs: int = 30, + resolution: float = 0.8, + n_neighbors: int = 20, + random_seed: int = 42, +) -> Dict[str, Any]: + """ + Performs spatial analysis using preprocessed spatial transcriptomics data and image features. + + This function simulates the 'spari analyze' command, taking a processed Seurat/SpatialExperiment + object and image-based features to perform clustering, dimensionality reduction, and + spatial domain identification. + + Args: + se_object_path: Path to the preprocessed Seurat/SpatialExperiment object (RDS file). + image_features_path: Path to the image features file (e.g., another RDS file or CSV). + output_dir: Directory to save analysis results (e.g., updated SE object, clustering results). + n_pcs: Number of principal components to use for dimensionality reduction. + resolution: Clustering resolution for graph-based clustering. + n_neighbors: Number of neighbors to use for UMAP/tSNE and graph construction. + random_seed: Seed for random number generation to ensure reproducibility. + + Returns: + A dictionary containing the command executed, stdout, stderr, and output files. + """ + # Input validation + if not se_object_path.is_file(): + raise ValueError(f"Seurat/SpatialExperiment object file '{se_object_path}' does not exist.") + if not image_features_path.is_file(): + raise ValueError(f"Image features file '{image_features_path}' does not exist.") + if se_object_path.suffix != ".rds": + raise ValueError(f"Input Seurat object '{se_object_path}' must have a '.rds' extension.") + if n_pcs <= 0: + raise ValueError("n_pcs must be a positive integer.") + if not (0 < resolution <= 5.0): # Common range for Seurat resolution + raise ValueError("resolution must be between 0 and 5.0.") + if n_neighbors <= 0: + raise ValueError("n_neighbors must be a positive integer.") + + output_dir.mkdir(parents=True, exist_ok=True) + output_se_object = output_dir / "analyzed_se_object.rds" + output_clusters_csv = output_dir / "cluster_assignments.csv" + + command = [ + "spari", "analyze", + "--se-object", str(se_object_path), + "--image-features", str(image_features_path), + "--output-dir", str(output_dir), + "--n-pcs", str(n_pcs), + "--resolution", str(resolution), + "--n-neighbors", str(n_neighbors), + "--seed", str(random_seed), + ] + + try: + process = subprocess.run(command, capture_output=True, text=True, check=True) + stdout = process.stdout + stderr = process.stderr + except FileNotFoundError: + raise RuntimeError("The 'spari' executable was not found. Please ensure it is installed and in your PATH.") + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(command), + "stdout": e.stdout, + "stderr": e.stderr, + "error": f"SPARi analysis failed with exit code {e.returncode}", + "output_files": [], + } + + # Assuming the tool creates these files + output_files = [str(output_se_object), str(output_clusters_csv)] + # Check if files were actually created by the hypothetical tool + output_files = [f for f in output_files if Path(f).exists()] + + return { + "command_executed": " ".join(command), + "stdout": stdout, + "stderr": stderr, + "output_files": output_files, + } + + +@mcp.tool() +def plot_spatial_results( + se_object_path: Path, + output_plot_path: Path, + plot_type: str, + feature_to_plot: Optional[str] = None, + cluster_id: Optional[str] = None, + width: int = 800, + height: int = 600, + plot_format: str = "png", +) -> Dict[str, Any]: + """ + Generates visualizations of spatial data and analysis results. + + This function simulates the 'spari plot' command, allowing users to visualize + spatial gene expression, clusters, or other features on the spatial transcriptomics + slide. + + Args: + se_object_path: Path to the analyzed Seurat/SpatialExperiment object (RDS file). + output_plot_path: Path to save the generated plot (e.g., PNG, PDF). + plot_type: Type of plot to generate. Valid options: "feature_plot", "spatial_dim_plot", "cluster_plot". + feature_to_plot: Name of the feature (e.g., gene name) to plot. Required for "feature_plot". + cluster_id: Identifier of a specific cluster to highlight or plot. + width: Width of the output plot in pixels. + height: Height of the output plot in pixels. + plot_format: Format of the output plot (e.g., "png", "pdf", "jpeg"). + + Returns: + A dictionary containing the command executed, stdout, stderr, and output files. + """ + # Input validation + if not se_object_path.is_file(): + raise ValueError(f"Seurat/SpatialExperiment object file '{se_object_path}' does not exist.") + if se_object_path.suffix != ".rds": + raise ValueError(f"Input Seurat object '{se_object_path}' must have a '.rds' extension.") + if plot_type not in ["feature_plot", "spatial_dim_plot", "cluster_plot"]: + raise ValueError(f"Unsupported plot_type: '{plot_type}'. " + "Choose from 'feature_plot', 'spatial_dim_plot', 'cluster_plot'.") + if plot_type == "feature_plot" and feature_to_plot is None: + raise ValueError("feature_to_plot is required for 'feature_plot' type.") + if width <= 0 or height <= 0: + raise ValueError("Plot width and height must be positive integers.") + if plot_format not in ["png", "pdf", "jpeg", "tiff"]: + raise ValueError(f"Unsupported plot_format: '{plot_format}'. " + "Choose from 'png', 'pdf', 'jpeg', 'tiff'.") + if output_plot_path.suffix.lower().lstrip('.') != plot_format.lower(): + raise ValueError(f"Output plot file extension '{output_plot_path.suffix}' does not match " + f"specified plot_format '{plot_format}'.") + + output_plot_path.parent.mkdir(parents=True, exist_ok=True) + + command = [ + "spari", "plot", + "--se-object", str(se_object_path), + "--output", str(output_plot_path), + "--type", plot_type, + "--width", str(width), + "--height", str(height), + "--format", plot_format, + ] + + if feature_to_plot: + command.extend(["--feature", feature_to_plot]) + if cluster_id: + command.extend(["--cluster-id", cluster_id]) + + try: + process = subprocess.run(command, capture_output=True, text=True, check=True) + stdout = process.stdout + stderr = process.stderr + except FileNotFoundError: + raise RuntimeError("The 'spari' executable was not found. Please ensure it is installed and in your PATH.") + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(command), + "stdout": e.stdout, + "stderr": e.stderr, + "error": f"SPARi plot failed with exit code {e.returncode}", + "output_files": [], + } + + # Assuming the tool creates the specified output plot file + output_files = [str(output_plot_path)] + output_files = [f for f in output_files if Path(f).exists()] + + return { + "command_executed": " ".join(command), + "stdout": stdout, + "stderr": stderr, + "output_files": output_files, + } \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-spari/app/bioconductor-spari_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-spari/app/bioconductor-spari_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..42f67f388264969eef66463d4db2ff5cd00241ed --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-spari/app/bioconductor-spari_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-spari/app/bioconductor-spari_server.py') +SERVER_NAME = 'biosci_bioconductor_spari' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-spari/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-spari/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-spari/app/requirements.txt @@ -0,0 +1 @@ + diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-spari/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-spari/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..90758242324926e6caa82b3eca9a1e302a008734 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-spari/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-bioconductor-spari: + build: . + image: mcp-bioconductor-spari:latest + container_name: mcp-bioconductor-spari + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=bioconductor-spari + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-spari/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-spari/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..82a1408ed8077cf3a62676a04cccf425f8d64344 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-spari/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - bioconductor-spari + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-spari/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-spari/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-spari/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-spatialfda/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-spatialfda/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..4e953a8080a0be08c686284f830f15529aacd419 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-spatialfda/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install bioconductor-spatialfda via conda (e.g., from bioconda) +RUN conda install -c bioconda bioconductor-spatialfda -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY app/bioconductor-spatialfda_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/bioconductor-spatialfda_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/bioconductor-spatialfda_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-spatialfda/app/bioconductor-spatialfda_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-spatialfda/app/bioconductor-spatialfda_server.py new file mode 100644 index 0000000000000000000000000000000000000000..1da427e12e662c306e541088477acd79a188a577 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-spatialfda/app/bioconductor-spatialfda_server.py @@ -0,0 +1,288 @@ +import subprocess +import os +from pathlib import Path +from typing import Optional, List, Dict, Any + +@mcp.tool() +def spatial_fda_fit( + expression_csv: str, + coords_csv: str, + output_rds: str, + nbasis: int = 10, + n_comp: int = 3, + lambda_val: float = 0.1, + basis_type: str = "bspline", +) -> Dict[str, Any]: + """ + Fits a Spatial Functional Data Analysis (spatialFDA) model to spatial transcriptomics or proteomics data. + + Args: + expression_csv: Path to a CSV file where rows are observations and columns are functional points (e.g., time or distance). + coords_csv: Path to a CSV file containing 'x' and 'y' spatial coordinates. + output_rds: Path to save the fitted model object (.rds). + nbasis: Number of basis functions for functional smoothing. + n_comp: Number of functional principal components to retain. + lambda_val: Smoothing parameter (penalty). + basis_type: Type of basis functions to use ('bspline' or 'fourier'). + """ + # Input validation + expr_path = Path(expression_csv) + coords_path = Path(coords_csv) + out_path = Path(output_rds) + + if not expr_path.exists(): + return {"error": f"Expression file not found: {expression_csv}"} + if not coords_path.exists(): + return {"error": f"Coordinates file not found: {coords_csv}"} + if nbasis <= 0: + return {"error": "nbasis must be a positive integer"} + if n_comp <= 0: + return {"error": "n_comp must be a positive integer"} + + # Construct R script + r_code = f""" + library(spatialFDA) + expr_data <- read.csv("{expr_path}") + coords_data <- read.csv("{coords_path}") + + # Ensure data is matrix + expr_mat <- as.matrix(expr_data) + coords_mat <- as.matrix(coords_data) + + # Fit model + model <- spatialFDA( + data = expr_mat, + coords = coords_mat, + nbasis = {nbasis}, + n_comp = {n_comp}, + lambda = {lambda_val}, + basis_type = "{basis_type}" + ) + + saveRDS(model, "{out_path}") + """ + + try: + result = subprocess.run( + ["Rscript", "-e", r_code], + capture_output=True, + text=True, + check=True + ) + return { + "command_executed": f"spatialFDA fit with {nbasis} basis functions", + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": [str(out_path)] + } + except subprocess.CalledProcessError as e: + return { + "error": "R execution failed", + "stdout": e.stdout, + "stderr": e.stderr + } + +@mcp.tool() +def spatial_fda_predict( + model_rds: str, + new_coords_csv: str, + output_csv: str, +) -> Dict[str, Any]: + """ + Predicts functional values at new spatial locations using a pre-trained spatialFDA model. + + Args: + model_rds: Path to the fitted spatialFDA model (.rds). + new_coords_csv: Path to CSV containing new 'x' and 'y' coordinates for prediction. + output_csv: Path to save the predicted functional values. + """ + model_path = Path(model_rds) + coords_path = Path(new_coords_csv) + out_path = Path(output_csv) + + if not model_path.exists(): + return {"error": f"Model file not found: {model_rds}"} + if not coords_path.exists(): + return {"error": f"Coordinates file not found: {new_coords_csv}"} + + r_code = f""" + library(spatialFDA) + model <- readRDS("{model_path}") + new_coords <- read.csv("{coords_path}") + + predictions <- predict(model, new_coords = as.matrix(new_coords)) + write.csv(predictions, "{out_path}", row.names = FALSE) + """ + + try: + result = subprocess.run( + ["Rscript", "-e", r_code], + capture_output=True, + text=True, + check=True + ) + return { + "command_executed": "spatialFDA prediction", + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": [str(out_path)] + } + except subprocess.CalledProcessError as e: + return { + "error": "R execution failed", + "stdout": e.stdout, + "stderr": e.stderr + } + +@mcp.tool() +def spatial_fda_fpca( + model_rds: str, + output_prefix: str, +) -> Dict[str, Any]: + """ + Extracts Functional Principal Component Analysis (FPCA) scores and eigenfunctions from the model. + + Args: + model_rds: Path to the fitted spatialFDA model (.rds). + output_prefix: Prefix for output CSV files (scores and eigenfunctions). + """ + model_path = Path(model_rds) + if not model_path.exists(): + return {"error": f"Model file not found: {model_rds}"} + + scores_out = f"{output_prefix}_scores.csv" + eigen_out = f"{output_prefix}_eigenfunctions.csv" + + r_code = f""" + library(spatialFDA) + model <- readRDS("{model_path}") + + # Extract FPCA components + scores <- model$fpca_scores + eigenfunctions <- model$eigenfunctions + + write.csv(scores, "{scores_out}", row.names = FALSE) + write.csv(eigenfunctions, "{eigen_out}", row.names = FALSE) + """ + + try: + result = subprocess.run( + ["Rscript", "-e", r_code], + capture_output=True, + text=True, + check=True + ) + return { + "command_executed": "spatialFDA FPCA extraction", + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": [scores_out, eigen_out] + } + except subprocess.CalledProcessError as e: + return { + "error": "R execution failed", + "stdout": e.stdout, + "stderr": e.stderr + } + +@mcp.tool() +def spatial_fda_summary_stats( + model_rds: str, +) -> Dict[str, Any]: + """ + Returns summary statistics of the fitted spatialFDA model, including variance explained. + + Args: + model_rds: Path to the fitted spatialFDA model (.rds). + """ + model_path = Path(model_rds) + if not model_path.exists(): + return {"error": f"Model file not found: {model_rds}"} + + r_code = f""" + library(spatialFDA) + model <- readRDS("{model_path}") + cat("--- Model Summary ---\\n") + cat("Number of Basis Functions:", model$nbasis, "\\n") + cat("Number of Components:", model$n_comp, "\\n") + cat("Variance Explained by Components:\\n") + print(model$var_explained) + """ + + try: + result = subprocess.run( + ["Rscript", "-e", r_code], + capture_output=True, + text=True, + check=True + ) + return { + "command_executed": "spatialFDA summary", + "stdout": result.stdout, + "stderr": result.stderr, + "model_info": result.stdout + } + except subprocess.CalledProcessError as e: + return { + "error": "R execution failed", + "stdout": e.stdout, + "stderr": e.stderr + } + +@mcp.tool() +def spatial_fda_plot( + model_rds: str, + plot_type: str = "eigenfunctions", + output_pdf: str = "spatial_fda_plots.pdf", +) -> Dict[str, Any]: + """ + Generates diagnostic and results plots for the spatialFDA model. + + Args: + model_rds: Path to the fitted spatialFDA model (.rds). + plot_type: Type of plot to generate ('eigenfunctions', 'scores', or 'reconstruction'). + output_pdf: Path to save the resulting plot as a PDF. + """ + model_path = Path(model_rds) + out_path = Path(output_pdf) + + if not model_path.exists(): + return {"error": f"Model file not found: {model_rds}"} + + valid_plots = ["eigenfunctions", "scores", "reconstruction"] + if plot_type not in valid_plots: + return {"error": f"Invalid plot_type. Must be one of: {', '.join(valid_plots)}"} + + r_code = f""" + library(spatialFDA) + model <- readRDS("{model_path}") + pdf("{out_path}") + if ("{plot_type}" == "eigenfunctions") {{ + plot(model, type = "eigenfunctions") + }} else if ("{plot_type}" == "scores") {{ + plot(model, type = "scores") + }} else {{ + plot(model, type = "reconstruction") + }} + dev.off() + """ + + try: + result = subprocess.run( + ["Rscript", "-e", r_code], + capture_output=True, + text=True, + check=True + ) + return { + "command_executed": f"spatialFDA plot: {plot_type}", + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": [str(out_path)] + } + except subprocess.CalledProcessError as e: + return { + "error": "R execution failed", + "stdout": e.stdout, + "stderr": e.stderr + } \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-spatialfda/app/bioconductor-spatialfda_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-spatialfda/app/bioconductor-spatialfda_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..ba7360585f4e6f00db857b12102163512100d59d --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-spatialfda/app/bioconductor-spatialfda_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-spatialfda/app/bioconductor-spatialfda_server.py') +SERVER_NAME = 'biosci_bioconductor_spatialfda' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-spatialfda/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-spatialfda/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-spatialfda/app/requirements.txt @@ -0,0 +1 @@ + diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-spatialfda/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-spatialfda/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..9efb8125ae1092edfc49a3a1d814191b8b3bd3a9 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-spatialfda/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-bioconductor-spatialfda: + build: . + image: mcp-bioconductor-spatialfda:latest + container_name: mcp-bioconductor-spatialfda + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=bioconductor-spatialfda + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-spatialfda/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-spatialfda/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..1b7a8a07517993c5588bf1e5fe4accb8295ec2b2 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-spatialfda/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - bioconductor-spatialfda + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-spatialfda/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-spatialfda/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-spatialfda/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-spatialfeatureexperiment/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-spatialfeatureexperiment/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..472f2be1e951bfd4d358c2f9125bc80c097d48e0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-spatialfeatureexperiment/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install bioconductor-spatialfeatureexperiment via conda (e.g., from bioconda) +RUN conda install -c bioconda bioconductor-spatialfeatureexperiment -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY app/bioconductor-spatialfeatureexperiment_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/bioconductor-spatialfeatureexperiment_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/bioconductor-spatialfeatureexperiment_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-spatialfeatureexperiment/app/bioconductor-spatialfeatureexperiment_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-spatialfeatureexperiment/app/bioconductor-spatialfeatureexperiment_server.py new file mode 100644 index 0000000000000000000000000000000000000000..5cae98e7423bc41f584de47155f553bf6c7f50bc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-spatialfeatureexperiment/app/bioconductor-spatialfeatureexperiment_server.py @@ -0,0 +1,118 @@ +import subprocess +import tempfile +import shutil +from pathlib import Path +from typing import List, Optional + +# MCP decorator placeholder. +# In a real MCP environment, this would be `from mcp import tool`. +def tool(*args, **kwargs): + def decorator(f): + return f + return decorator + +mcp = type("mcp", (), {"tool": tool}) + + +@mcp.tool() +def run_spatialfeatureexperiment_script( + script: Path, + input_files: Optional[List[Path]] = None, + script_args: Optional[List[str]] = None, +) -> dict: + """ + Executes a user-provided R script using the SpatialFeatureExperiment library. + + This tool serves as a generic wrapper to run custom R scripts in an environment + where the bioconductor-spatialfeatureexperiment package and its dependencies are + installed. The script should be written to read any necessary input files from + its working directory and write all output files to the same directory. + + Args: + script: Path to the R script to be executed. This script should load and + use functions from the SpatialFeatureExperiment library. + input_files: An optional list of input data files required by the R script. + These files will be copied to the script's working directory. + script_args: An optional list of string arguments to be passed to the + R script. These can be accessed within R using + `commandArgs(trailingOnly = TRUE)`. + + Returns: + A dictionary containing the executed command, stdout, stderr, and a list + of paths to the output files generated by the script. + """ + # 1. Input validation + if not script.is_file(): + raise FileNotFoundError(f"The provided R script does not exist: {script}") + + if input_files: + for f in input_files: + if not f.is_file(): + raise FileNotFoundError(f"Input file does not exist: {f}") + + # 2. Create a temporary directory for isolated execution + with tempfile.TemporaryDirectory() as temp_dir_str: + temp_dir = Path(temp_dir_str) + + # 3. Stage input files into the temporary directory + staged_script_path = temp_dir / script.name + shutil.copy(script, staged_script_path) + + if input_files: + for f in input_files: + shutil.copy(f, temp_dir / f.name) + + # Keep track of initial files to easily identify outputs later + initial_files = {p.name for p in temp_dir.iterdir()} + + # 4. Construct the command to be executed + command = ["Rscript", str(staged_script_path)] + if script_args: + command.extend(script_args) + + command_str = " ".join(command) + + # 5. Execute the R script + try: + result = subprocess.run( + command, + capture_output=True, + text=True, + check=True, + cwd=temp_dir, # Run the script within the temp directory + ) + stdout = result.stdout + stderr = result.stderr + except FileNotFoundError: + # This error occurs if 'Rscript' is not in the system's PATH + return { + "command_executed": command_str, + "stdout": "", + "stderr": "Error: 'Rscript' command not found. Make sure R is installed and in the system's PATH.", + "output_files": [] + } + except subprocess.CalledProcessError as e: + # This error occurs if the R script exits with a non-zero status code + return { + "command_executed": command_str, + "stdout": e.stdout, + "stderr": e.stderr, + "output_files": [] + } + + # 6. Identify and collect output files + # Any file created in the temp_dir that was not an input is an output + final_files = {p.name for p in temp_dir.iterdir()} + output_filenames = list(final_files - initial_files) + + # The MCP framework will handle moving these files from the temp dir + # to the final output location. We just need to provide their paths. + output_paths = [str(temp_dir / fname) for fname in output_filenames] + + # 7. Return structured output + return { + "command_executed": command_str, + "stdout": stdout, + "stderr": stderr, + "output_files": output_paths, + } \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-spatialfeatureexperiment/app/bioconductor-spatialfeatureexperiment_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-spatialfeatureexperiment/app/bioconductor-spatialfeatureexperiment_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..6cd069f7bafb54ef9fe6a23c857bfa329a431674 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-spatialfeatureexperiment/app/bioconductor-spatialfeatureexperiment_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-spatialfeatureexperiment/app/bioconductor-spatialfeatureexperiment_server.py') +SERVER_NAME = 'biosci_bioconductor_spatialfeatureexperiment' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-spatialfeatureexperiment/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-spatialfeatureexperiment/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-spatialfeatureexperiment/app/requirements.txt @@ -0,0 +1 @@ + diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-spatialfeatureexperiment/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-spatialfeatureexperiment/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..518b433cff1c61e3709e8ccb40a69f49b7e4eebd --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-spatialfeatureexperiment/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-bioconductor-spatialfeatureexperiment: + build: . + image: mcp-bioconductor-spatialfeatureexperiment:latest + container_name: mcp-bioconductor-spatialfeatureexperiment + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=bioconductor-spatialfeatureexperiment + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-spatialfeatureexperiment/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-spatialfeatureexperiment/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..df443e9ef61006cd0de22376498c186555b07420 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-spatialfeatureexperiment/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - bioconductor-spatialfeatureexperiment + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-spatialfeatureexperiment/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-spatialfeatureexperiment/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-spatialfeatureexperiment/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-speckle/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-speckle/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..a67a88fb59e6ffcb7d0c9ba4df47c11cfa2117fe --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-speckle/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install bioconductor-speckle via conda (e.g., from bioconda) +RUN conda install -c bioconda bioconductor-speckle -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY app/bioconductor-speckle_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/bioconductor-speckle_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/bioconductor-speckle_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-speckle/app/bioconductor-speckle_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-speckle/app/bioconductor-speckle_server.py new file mode 100644 index 0000000000000000000000000000000000000000..d2b15a6805ecb09e7e047a856906c9d5ae0c1971 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-speckle/app/bioconductor-speckle_server.py @@ -0,0 +1,434 @@ +import subprocess +import tempfile +from pathlib import Path +from typing import List, Literal, Optional + +# This is a placeholder for the MCP decorator. +# In a real MCP environment, this would be provided by the MCP framework. +def tool(func): + """A dummy decorator to mimic @mcp.tool() for standalone execution.""" + return func + +mcp = type("MCP", (), {"tool": tool}) + + +@mcp.tool() +def propeller( + input_object: Path, + clusters_col: str, + sample_col: str, + group_col: str, + output_results_csv: str = "propeller_results.csv", + transform: Literal["asin", "logit"] = "asin", + trend: bool = False, + robust: bool = True, + permutations: int = 100, + p_adjust_method: Literal[ + "holm", "hochberg", "hommel", "bonferroni", "BH", "BY", "fdr", "none" + ] = "BH", +) -> dict: + """ + Tests for differential cell type proportions between experimental groups using the propeller method. + + This function uses a t-test with permutations or an ANOVA to assess significance. + It requires a Seurat or SingleCellExperiment object as input. + + Args: + input_object: Path to the input Seurat or SingleCellExperiment object saved as an RDS file. + clusters_col: The column name in the object's metadata that contains cell type labels. + sample_col: The column name in the object's metadata that contains sample labels. + group_col: The column name in the object's metadata that contains the experimental group for each sample. + output_results_csv: Path for the output CSV file containing the differential proportion results. + transform: The transformation to use on the proportions ('asin' for arcsin-sqrt or 'logit'). + trend: If TRUE, allows for an intensity-trend in the prior variance. + robust: If TRUE, robustifies the estimation of the prior variance against outlier sample-level variances. + permutations: The number of permutations to perform for the t-test. + p_adjust_method: Method for multiple testing correction. + + Returns: + A dictionary containing the path to the output results file. + """ + # Input validation + if not input_object.is_file(): + raise FileNotFoundError(f"Input object file not found: {input_object}") + if permutations <= 0: + raise ValueError("Number of permutations must be a positive integer.") + + output_path = Path(output_results_csv) + + r_script_content = f""" + library(speckle) + library(SingleCellExperiment) + library(Seurat) + + # Load input object + message("Loading input object from: {input_object}") + x <- readRDS("{input_object}") + + # Extract group information + # Assumes group_col is present in the metadata associated with samples + if ("Seurat" %in% class(x)) {{ + sample_metadata <- unique(x@meta.data[, c("{sample_col}", "{group_col}")]) + }} else if ("SingleCellExperiment" %in% class(x)) {{ + sample_metadata <- as.data.frame(unique(colData(x)[, c("{sample_col}", "{group_col}")])) + }} else {{ + stop("Input object must be a Seurat or SingleCellExperiment object.") + }} + + # Ensure sample order matches for group vector + props_obj <- getTransformedProps(x, sample.col="{sample_col}", cell.type.col="{clusters_col}") + sample_order <- rownames(props_obj$Proportions) + group_vector <- sample_metadata[match(sample_order, sample_metadata${sample_col}), "{group_col}"] + + # Run propeller + message("Running propeller analysis...") + propeller_res <- propeller(x = x, + clusters = "{clusters_col}", + sample = "{sample_col}", + group = group_vector, + transform = "{transform}", + trend = {"TRUE" if trend else "FALSE"}, + robust = {"TRUE" if robust else "FALSE"}, + B = {permutations}, + p.adjust.method = "{p_adjust_method}") + + # Write results to CSV + message("Writing results to: {output_path}") + write.csv(propeller_res, file = "{output_path}", row.names = TRUE) + message("Analysis complete.") + """ + + with tempfile.NamedTemporaryFile(mode="w", suffix=".R", delete=False) as r_script_file: + r_script_file.write(r_script_content) + r_script_path = r_script_file.name + + command = ["Rscript", r_script_path] + command_executed = " ".join(command) + + try: + result = subprocess.run( + command, + capture_output=True, + text=True, + check=True, + cwd=str(output_path.parent), + ) + return { + "command_executed": command_executed, + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": [str(output_path)], + } + except subprocess.CalledProcessError as e: + return { + "command_executed": command_executed, + "stdout": e.stdout, + "stderr": e.stderr, + "error": "Speckle propeller failed.", + "return_code": e.returncode, + } + finally: + Path(r_script_path).unlink() + + +@mcp.tool() +def get_transformed_props( + input_object: Path, + sample_col: str, + cell_type_col: str, + output_proportions_csv: str = "proportions.csv", + output_counts_csv: str = "cell_counts.csv", + transform: Literal["asin", "logit"] = "asin", +) -> dict: + """ + Calculates cell type proportions and transforms them for linear modelling. + + Args: + input_object: Path to the input Seurat or SingleCellExperiment object saved as an RDS file. + sample_col: The column name in the object's metadata that contains sample labels. + cell_type_col: The column name in the object's metadata that contains cell type labels. + output_proportions_csv: Path for the output CSV file with transformed proportions. + output_counts_csv: Path for the output CSV file with raw cell counts per sample. + transform: The transformation to use on the proportions ('asin' for arcsin-sqrt or 'logit'). + + Returns: + A dictionary containing paths to the output files. + """ + if not input_object.is_file(): + raise FileNotFoundError(f"Input object file not found: {input_object}") + + output_proportions_path = Path(output_proportions_csv) + output_counts_path = Path(output_counts_csv) + + r_script_content = f""" + library(speckle) + library(SingleCellExperiment) + library(Seurat) + + message("Loading input object from: {input_object}") + x <- readRDS("{input_object}") + + message("Calculating and transforming proportions...") + props_list <- getTransformedProps(x, + sample.col = "{sample_col}", + cell.type.col = "{cell_type_col}", + transform = "{transform}") + + message("Writing transformed proportions to: {output_proportions_path}") + write.csv(props_list$Proportions, file = "{output_proportions_path}", row.names = TRUE) + + message("Writing cell counts to: {output_counts_path}") + write.csv(props_list$Counts, file = "{output_counts_path}", row.names = TRUE) + message("Process complete.") + """ + + with tempfile.NamedTemporaryFile(mode="w", suffix=".R", delete=False) as r_script_file: + r_script_file.write(r_script_content) + r_script_path = r_script_file.name + + command = ["Rscript", r_script_path] + command_executed = " ".join(command) + + try: + result = subprocess.run( + command, + capture_output=True, + text=True, + check=True, + cwd=str(output_proportions_path.parent), + ) + return { + "command_executed": command_executed, + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": [str(output_proportions_path), str(output_counts_path)], + } + except subprocess.CalledProcessError as e: + return { + "command_executed": command_executed, + "stdout": e.stdout, + "stderr": e.stderr, + "error": "Speckle get_transformed_props failed.", + "return_code": e.returncode, + } + finally: + Path(r_script_path).unlink() + + +@mcp.tool() +def normalize_to_median( + counts_matrix: Path, + output_matrix_csv: str = "normalized_counts.csv", +) -> dict: + """ + Normalizes a counts matrix to the median library size. + + Args: + counts_matrix: Path to a CSV file containing a numeric matrix of counts (genes x cells or cells x samples). + output_matrix_csv: Path for the output normalized counts CSV file. + + Returns: + A dictionary containing the path to the output normalized matrix. + """ + if not counts_matrix.is_file(): + raise FileNotFoundError(f"Counts matrix file not found: {counts_matrix}") + + output_path = Path(output_matrix_csv) + + r_script_content = f""" + library(speckle) + + message("Reading counts matrix from: {counts_matrix}") + counts <- as.matrix(read.csv("{counts_matrix}", row.names=1)) + + message("Normalizing to median library size...") + norm_counts <- normalizeToMedian(counts) + + message("Writing normalized counts to: {output_path}") + write.csv(norm_counts, file="{output_path}", row.names=TRUE) + message("Normalization complete.") + """ + + with tempfile.NamedTemporaryFile(mode="w", suffix=".R", delete=False) as r_script_file: + r_script_file.write(r_script_content) + r_script_path = r_script_file.name + + command = ["Rscript", r_script_path] + command_executed = " ".join(command) + + try: + result = subprocess.run( + command, + capture_output=True, + text=True, + check=True, + cwd=str(output_path.parent), + ) + return { + "command_executed": command_executed, + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": [str(output_path)], + } + except subprocess.CalledProcessError as e: + return { + "command_executed": command_executed, + "stdout": e.stdout, + "stderr": e.stderr, + "error": "Speckle normalize_to_median failed.", + "return_code": e.returncode, + } + finally: + Path(r_script_path).unlink() + + +@mcp.tool() +def plot_props( + input_object: Path, + sample_col: str, + cell_type_col: str, + output_plot: str = "proportions_plot.pdf", +) -> dict: + """ + Generates a barplot of cell type proportions for each sample. + + Args: + input_object: Path to the input Seurat or SingleCellExperiment object saved as an RDS file. + sample_col: The column name in the object's metadata that contains sample labels. + cell_type_col: The column name in the object's metadata that contains cell type labels. + output_plot: Path for the output plot file (e.g., .pdf, .png). + + Returns: + A dictionary containing the path to the output plot file. + """ + if not input_object.is_file(): + raise FileNotFoundError(f"Input object file not found: {input_object}") + + output_path = Path(output_plot) + + r_script_content = f""" + library(speckle) + library(ggplot2) + library(SingleCellExperiment) + library(Seurat) + + message("Loading input object from: {input_object}") + x <- readRDS("{input_object}") + + message("Generating proportions plot...") + p <- plotProps(x, + sample.col = "{sample_col}", + cell.type.col = "{cell_type_col}") + + message("Saving plot to: {output_path}") + ggsave("{output_path}", plot = p, device = tools::file_ext("{output_path}")) + message("Plotting complete.") + """ + + with tempfile.NamedTemporaryFile(mode="w", suffix=".R", delete=False) as r_script_file: + r_script_file.write(r_script_content) + r_script_path = r_script_file.name + + command = ["Rscript", r_script_path] + command_executed = " ".join(command) + + try: + result = subprocess.run( + command, + capture_output=True, + text=True, + check=True, + cwd=str(output_path.parent), + ) + return { + "command_executed": command_executed, + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": [str(output_path)], + } + except subprocess.CalledProcessError as e: + return { + "command_executed": command_executed, + "stdout": e.stdout, + "stderr": e.stderr, + "error": "Speckle plot_props failed.", + "return_code": e.returncode, + } + finally: + Path(r_script_path).unlink() + + +@mcp.tool() +def plot_cell_counts( + input_object: Path, + sample_col: str, + cell_type_col: str, + output_plot: str = "cell_counts_plot.pdf", +) -> dict: + """ + Generates a barplot of cell counts per sample and cell type. + + Args: + input_object: Path to the input Seurat or SingleCellExperiment object saved as an RDS file. + sample_col: The column name in the object's metadata that contains sample labels. + cell_type_col: The column name in the object's metadata that contains cell type labels. + output_plot: Path for the output plot file (e.g., .pdf, .png). + + Returns: + A dictionary containing the path to the output plot file. + """ + if not input_object.is_file(): + raise FileNotFoundError(f"Input object file not found: {input_object}") + + output_path = Path(output_plot) + + r_script_content = f""" + library(speckle) + library(ggplot2) + library(SingleCellExperiment) + library(Seurat) + + message("Loading input object from: {input_object}") + x <- readRDS("{input_object}") + + message("Generating cell counts plot...") + p <- plotCellCounts(x, + sample.col = "{sample_col}", + cell.type.col = "{cell_type_col}") + + message("Saving plot to: {output_path}") + ggsave("{output_path}", plot = p, device = tools::file_ext("{output_path}")) + message("Plotting complete.") + """ + + with tempfile.NamedTemporaryFile(mode="w", suffix=".R", delete=False) as r_script_file: + r_script_file.write(r_script_content) + r_script_path = r_script_file.name + + command = ["Rscript", r_script_path] + command_executed = " ".join(command) + + try: + result = subprocess.run( + command, + capture_output=True, + text=True, + check=True, + cwd=str(output_path.parent), + ) + return { + "command_executed": command_executed, + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": [str(output_path)], + } + except subprocess.CalledProcessError as e: + return { + "command_executed": command_executed, + "stdout": e.stdout, + "stderr": e.stderr, + "error": "Speckle plot_cell_counts failed.", + "return_code": e.returncode, + } + finally: + Path(r_script_path).unlink() \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-speckle/app/bioconductor-speckle_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-speckle/app/bioconductor-speckle_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..c8b3e07efecfb94de4998fd347a7e8cc95ec4a70 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-speckle/app/bioconductor-speckle_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-speckle/app/bioconductor-speckle_server.py') +SERVER_NAME = 'biosci_bioconductor_speckle' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-speckle/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-speckle/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-speckle/app/requirements.txt @@ -0,0 +1 @@ + diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-speckle/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-speckle/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..21c91bd420aeeb077c41f747d46c0766162bfd57 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-speckle/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-bioconductor-speckle: + build: . + image: mcp-bioconductor-speckle:latest + container_name: mcp-bioconductor-speckle + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=bioconductor-speckle + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-speckle/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-speckle/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..38e7db65381ab0a48595f3b57d3f04bac5175822 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-speckle/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - bioconductor-speckle + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-speckle/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-speckle/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-speckle/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-standr/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-standr/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..86f93dbc55057d04067ba580336f730c08da84c3 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-standr/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install bioconductor-standr via conda (e.g., from bioconda) +RUN conda install -c bioconda bioconductor-standr -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY app/bioconductor-standr_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/bioconductor-standr_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/bioconductor-standr_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-standr/app/bioconductor-standr_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-standr/app/bioconductor-standr_server.py new file mode 100644 index 0000000000000000000000000000000000000000..83c93c5d93914a69ae80768cb4b2da1faf70234e --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-standr/app/bioconductor-standr_server.py @@ -0,0 +1,241 @@ +import subprocess +import tempfile +import shlex +from pathlib import Path +from typing import Optional, List, Dict, Union + +def _format_r_list(items: Optional[str]) -> str: + """Formats a comma-separated string into an R character vector string.""" + if items is None: + return "NULL" + # Split, strip whitespace, and quote each item + item_list = [f'"{item.strip()}"' for item in items.split(',')] + return f"c({', '.join(item_list)})" + +def _format_r_value(value: Union[str, int, float, bool, None]) -> str: + """Formats a Python value into its R string representation.""" + if value is None: + return "NULL" + if isinstance(value, bool): + return "TRUE" if value else "FALSE" + if isinstance(value, str): + return f'"{value}"' + return str(value) + +# @mcp.tool() +def run_standr_analysis( + dcc_files_dir: Path, + pkc_file: Path, + sample_sheet: Path, + output_dir: Path, + min_segment_reads: Optional[int] = None, + min_nuclei: Optional[int] = None, + percent_aligned_threshold: Optional[float] = None, + percent_saturation_threshold: Optional[float] = None, + norm_method: str = "quant", + housekeeping_genes: Optional[str] = None, + batch_variable: Optional[str] = None, + correction_method: str = "RUV", + run_dim_reduction: bool = True, + run_clustering: bool = True, +) -> Dict[str, Union[str, List[str]]]: + """ + Runs a standard spatial transcriptomics analysis workflow for Nanostring GeoMx data using the standR Bioconductor package. + + This tool performs data loading, quality control, normalization, optional batch correction, + dimensionality reduction (UMAP), and clustering. + """ + # --- Input Validation --- + if not dcc_files_dir.is_dir(): + raise ValueError(f"Input DCC directory not found: {dcc_files_dir}") + if not pkc_file.is_file(): + raise ValueError(f"Input PKC file not found: {pkc_file}") + if not sample_sheet.is_file(): + raise ValueError(f"Input sample sheet file not found: {sample_sheet}") + + if norm_method not in ["quant", "neg", "hk"]: + raise ValueError(f"Invalid normalization method '{norm_method}'. Must be one of 'quant', 'neg', 'hk'.") + if norm_method == "hk" and housekeeping_genes is None: + raise ValueError("`housekeeping_genes` must be provided when `norm_method` is 'hk'.") + + if correction_method not in ["RUV", "limma"]: + raise ValueError(f"Invalid batch correction method '{correction_method}'. Must be 'RUV' or 'limma'.") + + if percent_aligned_threshold is not None and not (0 <= percent_aligned_threshold <= 100): + raise ValueError("`percent_aligned_threshold` must be between 0 and 100.") + if percent_saturation_threshold is not None and not (0 <= percent_saturation_threshold <= 100): + raise ValueError("`percent_saturation_threshold` must be between 0 and 100.") + + # --- File Path Handling --- + output_dir.mkdir(parents=True, exist_ok=True) + + # --- R Script Generation --- + r_script_content = f""" + # Load required libraries + suppressPackageStartupMessages(library(standR)) + suppressPackageStartupMessages(library(SpatialExperiment)) + suppressPackageStartupMessages(library(dplyr)) + suppressPackageStartupMessages(library(readr)) + suppressPackageStartupMessages(library(ggplot2)) + suppressPackageStartupMessages(library(tibble)) + + # --- Parameters --- + dcc_dir <- "{dcc_files_dir.resolve()}" + pkc_f <- "{pkc_file.resolve()}" + sample_sheet_f <- "{sample_sheet.resolve()}" + output_dir <- "{output_dir.resolve()}" + + # QC Parameters + min_seg_reads <- {_format_r_value(min_segment_reads)} + min_nuc <- {_format_r_value(min_nuclei)} + perc_aligned <- {_format_r_value(percent_aligned_threshold)} + perc_sat <- {_format_r_value(percent_saturation_threshold)} + + # Normalization Parameters + norm_m <- "{norm_method}" + hk_genes <- {_format_r_list(housekeeping_genes)} + + # Batch Correction Parameters + batch_var <- {_format_r_value(batch_variable)} + correction_m <- "{correction_method}" + + # Analysis Parameters + run_dim_red <- {_format_r_value(run_dim_reduction)} + run_clust <- {_format_r_value(run_clustering)} + + # --- Main Workflow --- + tryCatch({{ + # 1. Load data + message("Step 1: Loading GeoMx data...") + spe <- readGeoMx(dcc.files = dcc_dir, + pkc.files = pkc_f, + pheno.data.file = sample_sheet_f) + message("Data loading complete.") + + # 2. QC + message("Step 2: Performing Quality Control...") + qc_criteria <- list() + if (!is.null(min_seg_reads)) {{ qc_criteria$minSegmentReads <- min_seg_reads }} + if (!is.null(min_nuc)) {{ qc_criteria$minNuclei <- min_nuc }} + if (!is.null(perc_aligned)) {{ qc_criteria$percentAligned <- perc_aligned }} + if (!is.null(perc_sat)) {{ qc_criteria$percentSaturation <- perc_sat }} + + if (length(qc_criteria) > 0) {{ + spe <- geomx_qc(spe, qc.criteria = qc_criteria) + message("QC filtering applied.") + }} else {{ + message("No QC criteria specified, skipping filtering.") + }} + + # 3. Normalization + message(paste0("Step 3: Normalizing data using '", norm_m, "' method...")) + if (norm_m == "hk" && !is.null(hk_genes)) {{ + spe <- normalise(spe, norm.method = "hk", from.assay = "counts", to.assay = "norm", hk = hk_genes) + }} else if (norm_m == "neg") {{ + spe <- normalise(spe, norm.method = "neg", from.assay = "counts", to.assay = "norm") + }} else {{ + spe <- normalise(spe, norm.method = "quant", from.assay = "counts", to.assay = "norm") + }} + message("Normalization complete.") + + # 4. Batch Correction + final_assay <- "norm" + if (!is.null(batch_var)) {{ + message(paste0("Step 4: Applying batch correction on variable '", batch_var, "' using '", correction_m, "'...")) + spe <- batch_correct(spe, + batch = colData(spe)[[batch_var]], + from.assay = "norm", + to.assay = "norm_bc", + method = correction_m) + final_assay <- "norm_bc" + message("Batch correction complete.") + }} else {{ + message("Step 4: No batch variable provided, skipping batch correction.") + }} + + # 5. Dimensionality Reduction & Clustering + if (run_dim_red) {{ + message("Step 5a: Running UMAP for dimensionality reduction...") + spe <- dim_reduce(spe, from.assay = final_assay, method = "UMAP") + p_umap <- plot_dim_reduce(spe, colour.by = batch_var) + ggsave(file.path(output_dir, "UMAP_plot.pdf"), p_umap, width = 7, height = 6) + message("UMAP plot saved.") + }} + + if (run_clust) {{ + message("Step 5b: Running clustering...") + spe <- find_clusters(spe, from.assay = final_assay) + p_clust <- plot_dim_reduce(spe, colour.by = "clusters") + ggsave(file.path(output_dir, "UMAP_clusters_plot.pdf"), p_clust, width = 7, height = 6) + message("Clustering plot saved.") + }} + + # 6. Save results + message("Step 6: Saving output files...") + saveRDS(spe, file = file.path(output_dir, "final_spe_object.rds")) + + final_counts <- as.data.frame(assay(spe, final_assay)) + final_counts <- tibble::rownames_to_column(final_counts, "Gene") + write_csv(final_counts, file = file.path(output_dir, "final_processed_counts.csv")) + + write_csv(as.data.frame(colData(spe)), file = file.path(output_dir, "final_coldata.csv")) + message("Output files saved successfully.") + + message("\\nstandR workflow completed successfully.") + + }}, error = function(e) {{ + message("An error occurred during the standR workflow:") + message(e$message) + quit(status = 1, save = "no") + }}) + """ + + # --- Subprocess Execution --- + output_files = [] + try: + with tempfile.NamedTemporaryFile(mode='w', suffix=".R", delete=False) as f: + script_path = Path(f.name) + f.write(r_script_content) + + cmd = ["Rscript", str(script_path)] + command_executed = shlex.join(cmd) + + process = subprocess.run( + cmd, + check=True, + capture_output=True, + text=True, + ) + + # --- Collect Output Files --- + output_files = [ + str(output_dir / "final_spe_object.rds"), + str(output_dir / "final_processed_counts.csv"), + str(output_dir / "final_coldata.csv"), + ] + if run_dim_reduction: + output_files.append(str(output_dir / "UMAP_plot.pdf")) + if run_clustering: + output_files.append(str(output_dir / "UMAP_clusters_plot.pdf")) + + # Filter for files that actually exist + output_files = [p for p in output_files if Path(p).exists()] + + return { + "command_executed": command_executed, + "stdout": process.stdout, + "stderr": process.stderr, + "output_files": output_files, + } + + except subprocess.CalledProcessError as e: + return { + "command_executed": command_executed, + "stdout": e.stdout, + "stderr": e.stderr, + "output_files": [], + "error": f"R script execution failed with exit code {e.returncode}", + } + finally: + if 'script_path' in locals() and script_path.exists(): + script_path.unlink() diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-standr/app/bioconductor-standr_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-standr/app/bioconductor-standr_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..ff49585cacd40ee8079f46c3a985716bef530dc9 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-standr/app/bioconductor-standr_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-standr/app/bioconductor-standr_server.py') +SERVER_NAME = 'biosci_bioconductor_standr' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-standr/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-standr/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-standr/app/requirements.txt @@ -0,0 +1 @@ + diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-standr/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-standr/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..d7206c638d7159d286d43f36148bc70f38cd90c3 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-standr/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-bioconductor-standr: + build: . + image: mcp-bioconductor-standr:latest + container_name: mcp-bioconductor-standr + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=bioconductor-standr + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-standr/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-standr/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..80500636d4d664f54829cb7877154b020109c530 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-standr/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - bioconductor-standr + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-standr/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-standr/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-standr/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-statial/app/bioconductor-statial_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-statial/app/bioconductor-statial_server.py new file mode 100644 index 0000000000000000000000000000000000000000..996b07531068bd91825ef7f9779e12756212a704 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-statial/app/bioconductor-statial_server.py @@ -0,0 +1,313 @@ +import subprocess +from pathlib import Path +from typing import Optional, List, Union +import textwrap + +@mcp.tool() +def statial_calc_interactions( + input_rds: str, + output_rds: str, + image_col: str, + cell_type_col: str, + x_col: str = "x", + y_col: str = "y", + method: str = "radius", + radius: float = 100.0, + n_neighbors: int = 10, +): + """ + Calculates the localization of cell types (interactions) for each image in a SingleCellExperiment object. + + Args: + input_rds: Path to the input SingleCellExperiment object saved as an RDS file. + output_rds: Path where the resulting interactions data frame will be saved as an RDS file. + image_col: Column name in colData representing unique image identifiers. + cell_type_col: Column name in colData representing cell types. + x_col: Column name for the X coordinate. + y_col: Column name for the Y coordinate. + method: Method to define proximity ('radius' or 'knn'). + radius: The radius to use if method is 'radius'. + n_neighbors: The number of neighbors to use if method is 'knn'. + """ + input_path = Path(input_rds) + output_path = Path(output_rds) + + if not input_path.exists(): + return {"error": f"Input file {input_rds} does not exist."} + + if method not in ["radius", "knn"]: + return {"error": "Method must be either 'radius' or 'knn'."} + + r_script = textwrap.dedent(f""" + library(statial) + sce <- readRDS('{input_path}') + interactions <- calcInteractions( + sce, + group = '{image_col}', + cellType = '{cell_type_col}', + spatialCoords = c('{x_col}', '{y_col}'), + method = '{method}', + radius = {radius}, + n_neighbors = {n_neighbors} + ) + saveRDS(interactions, '{output_path}') + """) + + try: + result = subprocess.run( + ["Rscript", "-e", r_script], + capture_output=True, + text=True, + check=True + ) + return { + "command_executed": f"statial::calcInteractions on {input_rds}", + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": [str(output_path)] + } + except subprocess.CalledProcessError as e: + return { + "error": "R execution failed", + "stdout": e.stdout, + "stderr": e.stderr + } + +@mcp.tool() +def statial_get_spatial_context( + input_rds: str, + output_rds: str, + cell_type_col: str, + x_col: str = "x", + y_col: str = "y", + r: float = 100.0, + k: int = 3, + image_col: Optional[str] = None +): + """ + Defines spatial contexts by clustering cell type densities across the tissue. + + Args: + input_rds: Path to the input SingleCellExperiment object (RDS). + output_rds: Path to save the SCE object with spatial context assignments (RDS). + cell_type_col: Column name in colData representing cell types. + x_col: Column name for the X coordinate. + y_col: Column name for the Y coordinate. + r: Radius for calculating cell density. + k: Number of spatial contexts (clusters) to identify. + image_col: Optional column name for image IDs if processing multiple images. + """ + input_path = Path(input_rds) + output_path = Path(output_rds) + + if not input_path.exists(): + return {"error": f"Input file {input_rds} does not exist."} + + image_arg = f"'{image_col}'" if image_col else "NULL" + + r_script = textwrap.dedent(f""" + library(statial) + sce <- readRDS('{input_path}') + sce <- getSpatialContext( + sce, + cellType = '{cell_type_col}', + spatialCoords = c('{x_col}', '{y_col}'), + r = {r}, + k = {k}, + group = {image_arg} + ) + saveRDS(sce, '{output_path}') + """) + + try: + result = subprocess.run( + ["Rscript", "-e", r_script], + capture_output=True, + text=True, + check=True + ) + return { + "command_executed": f"statial::getSpatialContext on {input_rds}", + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": [str(output_path)] + } + except subprocess.CalledProcessError as e: + return { + "error": "R execution failed", + "stdout": e.stdout, + "stderr": e.stderr + } + +@mcp.tool() +def statial_bivariate_test( + input_rds: str, + output_csv: str, + image_col: str, + cell_type_col: str, + spatial_context_col: str, + x_col: str = "x", + y_col: str = "y" +): + """ + Performs statistical testing to identify interactions that change significantly across spatial contexts. + + Args: + input_rds: Path to the input SingleCellExperiment object (RDS). + output_csv: Path to save the statistical test results (CSV). + image_col: Column name for image IDs. + cell_type_col: Column name for cell types. + spatial_context_col: Column name for the spatial context (calculated via getSpatialContext). + x_col: Column name for the X coordinate. + y_col: Column name for the Y coordinate. + """ + input_path = Path(input_rds) + output_path = Path(output_csv) + + if not input_path.exists(): + return {"error": f"Input file {input_rds} does not exist."} + + r_script = textwrap.dedent(f""" + library(statial) + sce <- readRDS('{input_path}') + results <- bivariateTest( + sce, + group = '{image_col}', + cellType = '{cell_type_col}', + spatialContext = '{spatial_context_col}', + spatialCoords = c('{x_col}', '{y_col}') + ) + write.csv(results, '{output_path}', row.names = FALSE) + """) + + try: + result = subprocess.run( + ["Rscript", "-e", r_script], + capture_output=True, + text=True, + check=True + ) + return { + "command_executed": f"statial::bivariateTest on {input_rds}", + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": [str(output_path)] + } + except subprocess.CalledProcessError as e: + return { + "error": "R execution failed", + "stdout": e.stdout, + "stderr": e.stderr + } + +@mcp.tool() +def statial_impute_interactions( + input_rds: str, + output_rds: str, + image_col: str, + cell_type_col: str, + min_cells: int = 5 +): + """ + Imputes interaction values for images where certain cell types might be missing or rare. + + Args: + input_rds: Path to the interactions data frame (RDS) generated by calcInteractions. + output_rds: Path to save the imputed interactions data frame (RDS). + image_col: Column name for image IDs. + cell_type_col: Column name for cell types. + min_cells: Minimum number of cells required to avoid imputation. + """ + input_path = Path(input_rds) + output_path = Path(output_rds) + + if not input_path.exists(): + return {"error": f"Input file {input_rds} does not exist."} + + r_script = textwrap.dedent(f""" + library(statial) + interactions <- readRDS('{input_path}') + imputed <- imputeInteractions( + interactions, + group = '{image_col}', + cellType = '{cell_type_col}', + minCells = {min_cells} + ) + saveRDS(imputed, '{output_path}') + """) + + try: + result = subprocess.run( + ["Rscript", "-e", r_script], + capture_output=True, + text=True, + check=True + ) + return { + "command_executed": f"statial::imputeInteractions on {input_rds}", + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": [str(output_path)] + } + except subprocess.CalledProcessError as e: + return { + "error": "R execution failed", + "stdout": e.stdout, + "stderr": e.stderr + } + +@mcp.tool() +def statial_get_interactions( + input_rds: str, + output_csv: str, + image_id: Optional[str] = None +): + """ + Extracts interaction information from a SingleCellExperiment object or interaction data frame. + + Args: + input_rds: Path to the input RDS file. + output_csv: Path to save the extracted interactions (CSV). + image_id: Optional specific image ID to extract interactions for. + """ + input_path = Path(input_rds) + output_path = Path(output_csv) + + if not input_path.exists(): + return {"error": f"Input file {input_rds} does not exist."} + + image_filter = f"subset(interact, group == '{image_id}')" if image_id else "interact" + + r_script = textwrap.dedent(f""" + library(statial) + interact <- readRDS('{input_path}') + # If input is SCE, extract interactions if they exist + if(is(interact, 'SingleCellExperiment')) {{ + # Assuming interactions were stored in metadata or colData by user + # This is a helper to convert the object to a readable table + interact <- as.data.frame(colData(interact)) + }} + + result_df <- {image_filter} + write.csv(result_df, '{output_path}', row.names = FALSE) + """) + + try: + result = subprocess.run( + ["Rscript", "-e", r_script], + capture_output=True, + text=True, + check=True + ) + return { + "command_executed": f"Extracting interactions from {input_rds}", + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": [str(output_path)] + } + except subprocess.CalledProcessError as e: + return { + "error": "R execution failed", + "stdout": e.stdout, + "stderr": e.stderr + } \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-statial/app/bioconductor-statial_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-statial/app/bioconductor-statial_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..d16f5fba423dfe3bd55bd3a134885fb6d1deebe8 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-statial/app/bioconductor-statial_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-statial/app/bioconductor-statial_server.py') +SERVER_NAME = 'biosci_bioconductor_statial' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-statial/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-statial/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-statial/app/requirements.txt @@ -0,0 +1 @@ + diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-statial/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-statial/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..60b17f741833ee008465d427f86c4807c1cd2c16 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-statial/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-bioconductor-statial: + build: . + image: mcp-bioconductor-statial:latest + container_name: mcp-bioconductor-statial + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=bioconductor-statial + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-svp/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-svp/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..55d520017b65e7ab21c5d60c7882e80cb419d065 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-svp/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install bioconductor-svp via conda (e.g., from bioconda) +RUN conda install -c bioconda bioconductor-svp -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY app/bioconductor-svp_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/bioconductor-svp_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/bioconductor-svp_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-svp/app/bioconductor-svp_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-svp/app/bioconductor-svp_server.py new file mode 100644 index 0000000000000000000000000000000000000000..f3d67b8c842bde6bdafe1022a1b6d50ae070894d --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-svp/app/bioconductor-svp_server.py @@ -0,0 +1,250 @@ +import subprocess +import tempfile +from pathlib import Path +from typing import Dict + +# Note: The @mcp.tool decorator is expected to be provided by the MCP framework. +# This is a placeholder class to allow the code to be syntactically valid. +class mcp: + @staticmethod + def tool(func): + return func + +@mcp.tool +def svp_train( + training_data: Path, + feature_data: Path, + output_model: Path, + method: str = "rf", +) -> Dict: + """ + Trains a Sequence-based Variant Prediction (SVP) model. + + This tool wraps the `svp_train` function from the R Bioconductor package 'SVP'. + It takes training data and feature definitions to build a model, which is then + saved to a specified file. The R package 'SVP' and its dependencies must be + installed in the R environment. + + Args: + training_data: Path to the training data file (tab-separated format). + This file should contain variant information and their known classifications. + feature_data: Path to the feature data file (tab-separated format). + This file defines the features to be used for model training. + output_model: Path where the trained model object (.rds file) will be saved. + method: The training method to be used by the underlying 'caret' package. + Defaults to 'rf' (Random Forest). + + Returns: + A dictionary containing the command executed, stdout, stderr, and the path + to the output model file. + """ + # 1. Input Validation + if not training_data.is_file(): + raise FileNotFoundError(f"Training data file not found: {training_data}") + if not feature_data.is_file(): + raise FileNotFoundError(f"Feature data file not found: {feature_data}") + + output_model.parent.mkdir(parents=True, exist_ok=True) + + # 2. R Script Generation + r_script_content = f""" + # Suppress startup messages for cleaner output + suppressPackageStartupMessages(library(SVP)) + + # Get arguments from command line + args <- commandArgs(trailingOnly=TRUE) + training_file <- args[1] + feature_file <- args[2] + output_model_file <- args[3] + train_method <- args[4] + + # Read data - assuming tab-separated values with a header + tryCatch({{ + training <- read.delim(training_file, header=TRUE, stringsAsFactors=FALSE) + feature <- read.delim(feature_file, header=TRUE, stringsAsFactors=FALSE) + }}, error = function(e) {{ + stop(paste("Error reading input files:", e$message)) + }}) + + # Train model + print("Starting model training...") + tryCatch({{ + svp_model <- svp_train(training, feature, method=train_method) + }}, error = function(e) {{ + stop(paste("Error during model training:", e$message)) + }}) + + # Save model + tryCatch({{ + saveRDS(svp_model, file=output_model_file) + }}, error = function(e) {{ + stop(paste("Error saving the model file:", e$message)) + }}) + + print(paste("Model training complete. Model saved to:", output_model_file)) + """ + + # 3. Subprocess Execution + r_script_path = None + try: + with tempfile.NamedTemporaryFile(mode='w', suffix=".R", delete=False) as tmp_r_script: + tmp_r_script.write(r_script_content) + r_script_path = tmp_r_script.name + + cmd = [ + "Rscript", + r_script_path, + str(training_data.resolve()), + str(feature_data.resolve()), + str(output_model.resolve()), + method, + ] + command_executed = " ".join(cmd) + + result = subprocess.run( + cmd, + check=True, + capture_output=True, + text=True, + ) + + # 4. Structured Result Return + return { + "command_executed": command_executed, + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": {"model_file": str(output_model)}, + } + except FileNotFoundError: + raise RuntimeError("Rscript command not found. Is R installed and in your system's PATH?") + except subprocess.CalledProcessError as e: + raise RuntimeError( + f"SVP training failed with exit code {e.returncode}.\n" + f"Command: {e.cmd}\n" + f"Stdout: {e.stdout}\n" + f"Stderr: {e.stderr}" + ) + finally: + # Clean up the temporary R script + if r_script_path and Path(r_script_path).exists(): + Path(r_script_path).unlink() + + +@mcp.tool +def svp_predict( + model_file: Path, + testing_data: Path, + feature_data: Path, + output_predictions: Path, +) -> Dict: + """ + Predicts variant effects using a pre-trained SVP model. + + This tool wraps the `svp_predict` function from the R Bioconductor package 'SVP'. + It uses a trained model to predict the classification of new variants. The R package + 'SVP' and its dependencies must be installed in the R environment. + + Args: + model_file: Path to the trained SVP model file (.rds format), generated by `svp_train`. + testing_data: Path to the data file containing new variants to be classified (tab-separated format). + feature_data: Path to the feature data file (tab-separated format). This must be the same + or have the same structure as the one used for training. + output_predictions: Path where the output predictions (tab-separated file) will be saved. + + Returns: + A dictionary containing the command executed, stdout, stderr, and the path + to the output predictions file. + """ + # 1. Input Validation + if not model_file.is_file(): + raise FileNotFoundError(f"Model file not found: {model_file}") + if not testing_data.is_file(): + raise FileNotFoundError(f"Testing data file not found: {testing_data}") + if not feature_data.is_file(): + raise FileNotFoundError(f"Feature data file not found: {feature_data}") + + output_predictions.parent.mkdir(parents=True, exist_ok=True) + + # 2. R Script Generation + r_script_content = f""" + # Suppress startup messages for cleaner output + suppressPackageStartupMessages(library(SVP)) + + # Get arguments from command line + args <- commandArgs(trailingOnly=TRUE) + model_file <- args[1] + testing_file <- args[2] + feature_file <- args[3] + output_file <- args[4] + + # Load model and data + tryCatch({{ + svp_model <- readRDS(model_file) + testing <- read.delim(testing_file, header=TRUE, stringsAsFactors=FALSE) + feature <- read.delim(feature_file, header=TRUE, stringsAsFactors=FALSE) + }}, error = function(e) {{ + stop(paste("Error reading input files:", e$message)) + }}) + + # Make predictions + print("Starting prediction...") + tryCatch({{ + svp_prediction <- svp_predict(svp_model, testing, feature) + }}, error = function(e) {{ + stop(paste("Error during prediction:", e$message)) + }}) + + # Write predictions to file + tryCatch({{ + write.table(svp_prediction, file=output_file, sep="\\t", row.names=FALSE, quote=FALSE) + }}, error = function(e) {{ + stop(paste("Error writing prediction output file:", e$message)) + }}) + + print(paste("Prediction complete. Results saved to:", output_file)) + """ + + # 3. Subprocess Execution + r_script_path = None + try: + with tempfile.NamedTemporaryFile(mode='w', suffix=".R", delete=False) as tmp_r_script: + tmp_r_script.write(r_script_content) + r_script_path = tmp_r_script.name + + cmd = [ + "Rscript", + r_script_path, + str(model_file.resolve()), + str(testing_data.resolve()), + str(feature_data.resolve()), + str(output_predictions.resolve()), + ] + command_executed = " ".join(cmd) + + result = subprocess.run( + cmd, + check=True, + capture_output=True, + text=True, + ) + + # 4. Structured Result Return + return { + "command_executed": command_executed, + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": {"predictions_file": str(output_predictions)}, + } + except FileNotFoundError: + raise RuntimeError("Rscript command not found. Is R installed and in your system's PATH?") + except subprocess.CalledProcessError as e: + raise RuntimeError( + f"SVP prediction failed with exit code {e.returncode}.\n" + f"Command: {e.cmd}\n" + f"Stdout: {e.stdout}\n" + f"Stderr: {e.stderr}" + ) + finally: + # Clean up the temporary R script + if r_script_path and Path(r_script_path).exists(): + Path(r_script_path).unlink() \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-svp/app/bioconductor-svp_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-svp/app/bioconductor-svp_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..c7b5d7a0a74e9b07f47dbe66a585d15d2f31895c --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-svp/app/bioconductor-svp_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-svp/app/bioconductor-svp_server.py') +SERVER_NAME = 'biosci_bioconductor_svp' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-svp/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-svp/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-svp/app/requirements.txt @@ -0,0 +1 @@ + diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-svp/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-svp/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..deb678c44d45a833fb58861466267c1f9fcc3fc7 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-svp/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-bioconductor-svp: + build: . + image: mcp-bioconductor-svp:latest + container_name: mcp-bioconductor-svp + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=bioconductor-svp + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-svp/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-svp/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..8f48187327f038c5fc31b8a5b5753cbd1ef47b1c --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-svp/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - bioconductor-svp + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-svp/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-svp/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-svp/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-tenxvisiumdata/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-tenxvisiumdata/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..a6923eb7656e2bbe5d0dade84049b3c8f40e5046 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-tenxvisiumdata/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install bioconductor-tenxvisiumdata via conda (e.g., from bioconda) +RUN conda install -c bioconda bioconductor-tenxvisiumdata -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY app/bioconductor-tenxvisiumdata_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/bioconductor-tenxvisiumdata_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/bioconductor-tenxvisiumdata_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-tenxvisiumdata/app/bioconductor-tenxvisiumdata_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-tenxvisiumdata/app/bioconductor-tenxvisiumdata_server.py new file mode 100644 index 0000000000000000000000000000000000000000..b02205f40e832914ad576d1f1ca3831ad669ef85 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-tenxvisiumdata/app/bioconductor-tenxvisiumdata_server.py @@ -0,0 +1,125 @@ +import subprocess +import tempfile +from pathlib import Path +from typing import Optional, List, Dict, Any + +# The mcp.tool decorator is supplied by the MCP framework. +# It is not needed for the code to be syntactically correct. +# We will assume it is imported from a module named "mcp". +# from mcp import tool as mcp_tool +# +# As per the instructions, we will define the function without the import. +# The user of this code will need to provide the mcp.tool decorator. + +# @mcp.tool +def tenxvisiumdata( + dataset: Optional[str] = None, + output_rds: Optional[Path] = None, +) -> Dict[str, Any]: + """ + Accesses Visium spatial gene expression data from 10X Genomics. + + This tool can either list all available datasets or download a specific dataset + as a SpatialExperiment object saved in an RDS file. + + - To list datasets: Call the function without any parameters. + - To download a dataset: Provide both the 'dataset' name and an 'output_rds' path. + """ + # --- Input Validation --- + if (dataset is None and output_rds is not None) or \ + (dataset is not None and output_rds is None): + raise ValueError( + "Both 'dataset' and 'output_rds' must be provided together to download data, or both must be omitted to list available datasets." + ) + + # --- R Script Generation --- + r_script_content = "" + cmd_args: List[str] = [] + + if dataset and output_rds: + # --- Download Mode --- + # Ensure the output directory exists + output_rds.parent.mkdir(parents=True, exist_ok=True) + + r_script_content = f""" + # Ensure required libraries are loaded + if (!requireNamespace("TENxVisiumData", quietly = TRUE)) {{ + stop("R package 'TENxVisiumData' is not installed. Please install it from Bioconductor.", call. = FALSE) + }} + library(TENxVisiumData) + + # Get arguments from the command line + args <- commandArgs(trailingOnly=TRUE) + if (length(args) != 2) {{ + stop("Internal error: Expected 2 arguments: ", call. = FALSE) + }} + dataset_name <- args[1] + output_path <- args[2] + + # Download the dataset from ExperimentHub + message("Downloading dataset: ", dataset_name) + spe_object <- TENxVisiumData(dataset = dataset_name) + message("Download complete.") + + # Save the SpatialExperiment object to an RDS file + message("Saving object to: ", output_path) + saveRDS(spe_object, file = output_path) + message("Save complete. Output file is ready at ", output_path) + """ + cmd_args = [dataset, str(output_rds)] + else: + # --- List Mode --- + r_script_content = """ + # Ensure required libraries are loaded + if (!requireNamespace("TENxVisiumData", quietly = TRUE)) { + stop("R package 'TENxVisiumData' is not installed. Please install it from Bioconductor.", call. = FALSE) + } + library(TENxVisiumData) + + # Get the table of available datasets from ExperimentHub + message("Fetching list of available datasets...") + available_datasets <- TENxVisiumData() + + # Print the relevant columns to stdout for parsing + print(available_datasets[, c("EHID", "Title", "RDataPath")]) + """ + + # --- Subprocess Execution --- + with tempfile.NamedTemporaryFile(mode='w', suffix=".R", delete=False) as r_script_file: + r_script_file.write(r_script_content) + r_script_path = r_script_file.name + + command = ["Rscript", r_script_path] + cmd_args + command_executed = " ".join(command) + + try: + result = subprocess.run( + command, + capture_output=True, + text=True, + check=True, + ) + except FileNotFoundError: + raise RuntimeError("Rscript executable not found. Please ensure R is installed and in your system's PATH.") + except subprocess.CalledProcessError as e: + # R scripts often print errors to stderr, so we return it for debugging + return { + "command_executed": command_executed, + "stdout": e.stdout, + "stderr": e.stderr, + "return_code": e.returncode, + "output_files": [], + } + finally: + # Clean up the temporary R script file + Path(r_script_path).unlink() + + # --- Structured Result Return --- + output_files = [str(output_rds)] if output_rds else [] + + return { + "command_executed": command_executed, + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": output_files, + } \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-tenxvisiumdata/app/bioconductor-tenxvisiumdata_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-tenxvisiumdata/app/bioconductor-tenxvisiumdata_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..3194ff5f2b96c36429cdf7f8f7cc1fca0680ad4c --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-tenxvisiumdata/app/bioconductor-tenxvisiumdata_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-tenxvisiumdata/app/bioconductor-tenxvisiumdata_server.py') +SERVER_NAME = 'biosci_bioconductor_tenxvisiumdata' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-tenxvisiumdata/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-tenxvisiumdata/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-tenxvisiumdata/app/requirements.txt @@ -0,0 +1 @@ + diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-tenxvisiumdata/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-tenxvisiumdata/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..715a81bd3eb249ba92251f09bca90b444af71b45 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-tenxvisiumdata/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-bioconductor-tenxvisiumdata: + build: . + image: mcp-bioconductor-tenxvisiumdata:latest + container_name: mcp-bioconductor-tenxvisiumdata + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=bioconductor-tenxvisiumdata + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-tenxvisiumdata/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-tenxvisiumdata/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..91fbb8ace2d32e250e876f3072bf8f346e0d80b0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-tenxvisiumdata/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - bioconductor-tenxvisiumdata + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-tenxvisiumdata/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-tenxvisiumdata/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-tenxvisiumdata/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-tenxxeniumdata/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-tenxxeniumdata/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..d59501990993d1eeeb40a8a91a09ed7342475cc9 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-tenxxeniumdata/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install bioconductor-tenxxeniumdata via conda (e.g., from bioconda) +RUN conda install -c bioconda bioconductor-tenxxeniumdata -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY app/bioconductor-tenxxeniumdata_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/bioconductor-tenxxeniumdata_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/bioconductor-tenxxeniumdata_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-tenxxeniumdata/app/bioconductor-tenxxeniumdata_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-tenxxeniumdata/app/bioconductor-tenxxeniumdata_server.py new file mode 100644 index 0000000000000000000000000000000000000000..5b7e7081d13be743a1d5d5dea073b2fcea24c106 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-tenxxeniumdata/app/bioconductor-tenxxeniumdata_server.py @@ -0,0 +1,143 @@ +import subprocess +from pathlib import Path +from typing import Optional, List +import logging + +# Configure logging +logging.basicConfig(level=logging.INFO) +logger = logging.getLogger(__name__) + +# MCP decorator is assumed to be available in the execution environment. +# No need to import it. +# @mcp.tool() + +@mcp.tool() +def load_tenx_xenium_data( + dataset: str, + output_rds: Path, + cache_dir: Optional[Path] = None, +): + """ + Loads a 10x Genomics Xenium dataset from the TENxXeniumData Bioconductor package. + + This tool fetches a specified dataset using ExperimentHub and saves it as an + R Data Serializable (RDS) file. The resulting object is either a SpatialExperiment + or a SpatialFeatureExperiment (SFE) class from Bioconductor. + + Args: + dataset (str): The name of the dataset to load. Must be one of the following: + - "Xenium_Human_Breast_Cancer_Rep1_SFE" + - "Xenium_Human_Breast_Cancer_Rep1_SE" + - "Xenium_Human_Breast_Cancer_Rep2_SFE" + - "Xenium_Human_Breast_Cancer_Rep2_SE" + - "Xenium_Human_Lung_Cancer_Rep1_SFE" + - "Xenium_Human_Lung_Cancer_Rep1_SE" + - "Xenium_Mouse_Brain_Coronal_s1_SFE" + - "Xenium_Mouse_Brain_Coronal_s1_SE" + - "Xenium_Mouse_Brain_Posterior_s1_SFE" + - "Xenium_Mouse_Brain_Posterior_s1_SE" + - "Xenium_Mouse_Puja_Brain_SFE" + - "Xenium_Mouse_Puja_Brain_SE" + output_rds (Path): The path to save the output RDS file. + cache_dir (Optional[Path]): An optional directory for ExperimentHub to cache the downloaded data. + If not provided, the default ExperimentHub cache location will be used. + + Returns: + dict: A dictionary containing the execution details and output file path. + """ + # 1. Input Validation + available_datasets = [ + "Xenium_Human_Breast_Cancer_Rep1_SFE", + "Xenium_Human_Breast_Cancer_Rep1_SE", + "Xenium_Human_Breast_Cancer_Rep2_SFE", + "Xenium_Human_Breast_Cancer_Rep2_SE", + "Xenium_Human_Lung_Cancer_Rep1_SFE", + "Xenium_Human_Lung_Cancer_Rep1_SE", + "Xenium_Mouse_Brain_Coronal_s1_SFE", + "Xenium_Mouse_Brain_Coronal_s1_SE", + "Xenium_Mouse_Brain_Posterior_s1_SFE", + "Xenium_Mouse_Brain_Posterior_s1_SE", + "Xenium_Mouse_Puja_Brain_SFE", + "Xenium_Mouse_Puja_Brain_SE", + ] + if dataset not in available_datasets: + raise ValueError( + f"Invalid dataset '{dataset}'. Must be one of: {', '.join(available_datasets)}" + ) + + # 2. File Path Handling + try: + output_rds.parent.mkdir(parents=True, exist_ok=True) + if cache_dir: + cache_dir.mkdir(parents=True, exist_ok=True) + except OSError as e: + logger.error(f"Error creating directory: {e}") + raise + + # 3. Construct R script for execution + r_script_lines = [ + 'suppressPackageStartupMessages(library(TENxXeniumData))', + 'suppressPackageStartupMessages(library(ExperimentHub))', + ] + + if cache_dir: + # R paths need to be correctly formatted (forward slashes) + r_cache_path = str(cache_dir.resolve()).replace('\\', '/') + r_script_lines.append(f'setExperimentHubOption("CACHE", "{r_cache_path}")') + + r_output_path = str(output_rds.resolve()).replace('\\', '/') + r_script_lines.extend([ + f'data_object <- {dataset}()', + f'saveRDS(data_object, file = "{r_output_path}")', + 'cat("Successfully loaded and saved dataset to RDS file.\\n")' + ]) + + r_script = "; ".join(r_script_lines) + + # 4. Subprocess Execution + cmd = ["Rscript", "-e", r_script] + command_executed = " ".join(cmd) + logger.info(f"Executing command: {command_executed}") + + try: + result = subprocess.run( + cmd, + capture_output=True, + text=True, + check=True, + ) + except FileNotFoundError: + # Handle case where Rscript is not in PATH + error_message = "Error: Rscript not found. Please ensure R is installed and in your system's PATH." + logger.error(error_message) + return { + "command_executed": command_executed, + "stdout": "", + "stderr": error_message, + "output_files": [], + "return_code": 1 + } + except subprocess.CalledProcessError as e: + logger.error(f"R script execution failed with return code {e.returncode}") + logger.error(f"Stderr: {e.stderr}") + logger.error(f"Stdout: {e.stdout}") + return { + "command_executed": command_executed, + "stdout": e.stdout, + "stderr": e.stderr, + "output_files": [], + "return_code": e.returncode + } + + # 5. Structured Result Return + output_files = [str(output_rds)] if output_rds.exists() else [] + if not output_files: + logger.warning(f"Output file {output_rds} was not created.") + + return { + "command_executed": command_executed, + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": output_files, + "return_code": result.returncode + } \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-tenxxeniumdata/app/bioconductor-tenxxeniumdata_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-tenxxeniumdata/app/bioconductor-tenxxeniumdata_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..77bb3ba4b37f66ad242e5e4554c9ccbf06d777e5 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-tenxxeniumdata/app/bioconductor-tenxxeniumdata_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-tenxxeniumdata/app/bioconductor-tenxxeniumdata_server.py') +SERVER_NAME = 'biosci_bioconductor_tenxxeniumdata' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-tenxxeniumdata/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-tenxxeniumdata/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-tenxxeniumdata/app/requirements.txt @@ -0,0 +1 @@ + diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-tenxxeniumdata/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-tenxxeniumdata/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..e2167c28c7b4737a4b32594ef03d88c34d6db8b9 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-tenxxeniumdata/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-bioconductor-tenxxeniumdata: + build: . + image: mcp-bioconductor-tenxxeniumdata:latest + container_name: mcp-bioconductor-tenxxeniumdata + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=bioconductor-tenxxeniumdata + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-tenxxeniumdata/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-tenxxeniumdata/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..f34874e474d51c77838554d6d2270d5488ada46a --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-tenxxeniumdata/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - bioconductor-tenxxeniumdata + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-tenxxeniumdata/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-tenxxeniumdata/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-tenxxeniumdata/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-vectrapolarisdata/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-vectrapolarisdata/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..60978ac86d56ab84f8d652b5b20c6a6efbf5c372 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-vectrapolarisdata/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install bioconductor-vectrapolarisdata via conda (e.g., from bioconda) +RUN conda install -c bioconda bioconductor-vectrapolarisdata -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY app/bioconductor-vectrapolarisdata_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/bioconductor-vectrapolarisdata_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/bioconductor-vectrapolarisdata_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-vectrapolarisdata/app/bioconductor-vectrapolarisdata_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-vectrapolarisdata/app/bioconductor-vectrapolarisdata_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..4c2f2f21f07e15d13659a834d346f76c54758009 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-vectrapolarisdata/app/bioconductor-vectrapolarisdata_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-vectrapolarisdata/app/bioconductor-vectrapolarisdata_server.py') +SERVER_NAME = 'biosci_bioconductor_vectrapolarisdata' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-vectrapolarisdata/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-vectrapolarisdata/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-vectrapolarisdata/app/requirements.txt @@ -0,0 +1 @@ + diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-vectrapolarisdata/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-vectrapolarisdata/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..e13bfc94b01eb2fbaca4f038d84e6c564e0ad203 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-vectrapolarisdata/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-bioconductor-vectrapolarisdata: + build: . + image: mcp-bioconductor-vectrapolarisdata:latest + container_name: mcp-bioconductor-vectrapolarisdata + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=bioconductor-vectrapolarisdata + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-vectrapolarisdata/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-vectrapolarisdata/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-vectrapolarisdata/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-xenlite/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-xenlite/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..bbd4d4ba8469b277ec49c242cb72079dc285911e --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-xenlite/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install bioconductor-xenlite via conda (e.g., from bioconda) +RUN conda install -c bioconda bioconductor-xenlite -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY app/bioconductor-xenlite_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/bioconductor-xenlite_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/bioconductor-xenlite_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-xenlite/app/bioconductor-xenlite_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-xenlite/app/bioconductor-xenlite_server.py new file mode 100644 index 0000000000000000000000000000000000000000..1a121d2478b314d82113fc52d5003705ef2db334 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-xenlite/app/bioconductor-xenlite_server.py @@ -0,0 +1,303 @@ +import subprocess +from pathlib import Path +from typing import Optional, List, Dict, Any + +@mcp.tool() +def xenlite_import_xenium( + data_dir: str, + output_rds: str, +) -> Dict[str, Any]: + """ + Imports Xenium spatial transcriptomics data from a directory and saves it as a xenLite R object. + + Args: + data_dir: Path to the directory containing Xenium output files (e.g., transcripts.parquet, cell_feature_matrix.h5). + output_rds: Path where the resulting xenLite RDS file will be saved. + """ + input_path = Path(data_dir) + output_path = Path(output_rds) + + if not input_path.is_dir(): + return {"error": f"Input directory does not exist: {data_dir}"} + + # Ensure output directory exists + output_path.parent.mkdir(parents=True, exist_ok=True) + + # R script to load the data using xenLite + r_script = f""" + library(xenLite) + tryCatch({{ + # The package typically uses a constructor to point to the Xenium output directory + # Based on documentation, it manages parquet and SpatialExperiment components + x_obj <- XeniumExperiment("{str(input_path)}") + saveRDS(x_obj, "{str(output_path)}") + cat("Successfully imported Xenium data to {str(output_path)}") + }}, error = function(e) {{ + stop(e$message) + }}) + """ + + try: + result = subprocess.run( + ["Rscript", "-e", r_script], + capture_output=True, + text=True, + check=True + ) + return { + "command_executed": f"Rscript -e 'library(xenLite); ...'", + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": [str(output_path)] + } + except subprocess.CalledProcessError as e: + return { + "error": "R execution failed", + "stdout": e.stdout, + "stderr": e.stderr, + "command_executed": e.cmd + } + +@mcp.tool() +def xenlite_to_spatial_experiment( + rds_path: str, + output_rds: str, +) -> Dict[str, Any]: + """ + Converts a xenLite object into a standard Bioconductor SpatialExperiment object. + + Args: + rds_path: Path to the xenLite RDS file. + output_rds: Path where the resulting SpatialExperiment RDS file will be saved. + """ + input_path = Path(rds_path) + output_path = Path(output_rds) + + if not input_path.exists(): + return {"error": f"Input RDS file not found: {rds_path}"} + + r_script = f""" + library(xenLite) + library(SpatialExperiment) + tryCatch({{ + x_obj <- readRDS("{str(input_path)}") + # xenLite objects are designed to be easily converted to SpatialExperiment + spe <- as(x_obj, "SpatialExperiment") + saveRDS(spe, "{str(output_path)}") + cat("Successfully converted to SpatialExperiment") + }}, error = function(e) {{ + stop(e$message) + }}) + """ + + try: + result = subprocess.run( + ["Rscript", "-e", r_script], + capture_output=True, + text=True, + check=True + ) + return { + "command_executed": "Rscript -e '...as(obj, \"SpatialExperiment\")...'", + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": [str(output_path)] + } + except subprocess.CalledProcessError as e: + return { + "error": "Conversion failed", + "stdout": e.stdout, + "stderr": e.stderr + } + +@mcp.tool() +def xenlite_extract_transcripts( + rds_path: str, + output_csv: str, + gene_filter: Optional[List[str]] = None, +) -> Dict[str, Any]: + """ + Extracts transcript coordinates from a xenLite object and saves them to a CSV file. + + Args: + rds_path: Path to the xenLite RDS file. + output_csv: Path to save the transcript coordinates (CSV). + gene_filter: Optional list of gene names to filter the transcripts. + """ + input_path = Path(rds_path) + output_path = Path(output_csv) + + if not input_path.exists(): + return {"error": f"Input RDS file not found: {rds_path}"} + + gene_filter_r = "NULL" + if gene_filter: + genes_str = ", ".join([f'"{g}"' for g in gene_filter]) + gene_filter_r = f"c({genes_str})" + + r_script = f""" + library(xenLite) + library(dplyr) + tryCatch({{ + x_obj <- readRDS("{str(input_path)}") + # Access transcripts (usually stored in parquet via arrow) + tx <- transcripts(x_obj) + + genes <- {gene_filter_r} + if (!is.null(genes)) {{ + tx <- tx %>% filter(feature_name %in% genes) + }} + + write.csv(as.data.frame(tx), "{str(output_path)}", row.names = FALSE) + cat("Extracted transcripts to {str(output_path)}") + }}, error = function(e) {{ + stop(e$message) + }}) + """ + + try: + result = subprocess.run( + ["Rscript", "-e", r_script], + capture_output=True, + text=True, + check=True + ) + return { + "command_executed": "Rscript -e '...transcripts(x_obj)...'", + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": [str(output_path)] + } + except subprocess.CalledProcessError as e: + return { + "error": "Transcript extraction failed", + "stdout": e.stdout, + "stderr": e.stderr + } + +@mcp.tool() +def xenlite_get_summary( + rds_path: str, +) -> Dict[str, Any]: + """ + Returns a summary of the Xenium dataset stored in a xenLite object. + + Args: + rds_path: Path to the xenLite RDS file. + """ + input_path = Path(rds_path) + + if not input_path.exists(): + return {"error": f"Input RDS file not found: {rds_path}"} + + r_script = f""" + library(xenLite) + tryCatch({{ + x_obj <- readRDS("{str(input_path)}") + print(x_obj) + cat("\\n--- Assay Data ---\\n") + spe <- as(x_obj, "SpatialExperiment") + cat("Number of cells: ", ncol(spe), "\\n") + cat("Number of genes: ", nrow(spe), "\\n") + cat("Assays: ", paste(assayNames(spe), collapse=", "), "\\n") + }}, error = function(e) {{ + stop(e$message) + }}) + """ + + try: + result = subprocess.run( + ["Rscript", "-e", r_script], + capture_output=True, + text=True, + check=True + ) + return { + "command_executed": "Rscript -e 'print(x_obj)'", + "stdout": result.stdout, + "stderr": result.stderr + } + except subprocess.CalledProcessError as e: + return { + "error": "Summary generation failed", + "stdout": e.stdout, + "stderr": e.stderr + } + +@mcp.tool() +def xenlite_plot_spatial( + rds_path: str, + output_png: str, + gene_name: Optional[str] = None, + sample_id: str = "sample01", +) -> Dict[str, Any]: + """ + Generates a spatial plot of the Xenium data. + + Args: + rds_path: Path to the xenLite RDS file. + output_png: Path to save the plot image (PNG). + gene_name: Optional gene name to color by expression. + sample_id: The sample identifier within the object. + """ + input_path = Path(rds_path) + output_path = Path(output_png) + + if not input_path.exists(): + return {"error": f"Input RDS file not found: {rds_path}"} + + gene_expr_r = "" + if gene_name: + gene_expr_r = f', colour_by = "{gene_name}"' + + r_script = f""" + library(xenLite) + library(SpatialExperiment) + library(ggplot2) + tryCatch({{ + x_obj <- readRDS("{str(input_path)}") + spe <- as(x_obj, "SpatialExperiment") + + # Basic spatial plot using ggplot2 as suggested by dependencies + df <- as.data.frame(cbind(spatialCoords(spe), colData(spe))) + + if ("{gene_name}" != "NULL" && "{gene_name}" %in% rownames(spe)) {{ + df$expression <- assay(spe, "counts")["{gene_name}", ] + p <- ggplot(df, aes(x = x_location, y = y_location, color = expression)) + + geom_point(size = 0.1) + + scale_color_viridis_c() + + theme_minimal() + + labs(title = paste("Expression of", "{gene_name}")) + }} else {{ + p <- ggplot(df, aes(x = x_location, y = y_location)) + + geom_point(size = 0.1, color = "lightgrey") + + theme_minimal() + + labs(title = "Spatial Cell Locations") + }} + + ggsave("{str(output_path)}", plot = p, width = 10, height = 8) + cat("Plot saved to {str(output_path)}") + }}, error = function(e) {{ + stop(e$message) + }}) + """ + + try: + result = subprocess.run( + ["Rscript", "-e", r_script], + capture_output=True, + text=True, + check=True + ) + return { + "command_executed": "Rscript -e 'ggplot2 plotting...'", + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": [str(output_path)] + } + except subprocess.CalledProcessError as e: + return { + "error": "Plotting failed", + "stdout": e.stdout, + "stderr": e.stderr + } \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-xenlite/app/bioconductor-xenlite_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-xenlite/app/bioconductor-xenlite_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..bf4cd2243db3a5ab152db122cf89f54d6c883eee --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-xenlite/app/bioconductor-xenlite_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-xenlite/app/bioconductor-xenlite_server.py') +SERVER_NAME = 'biosci_bioconductor_xenlite' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-xenlite/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-xenlite/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-xenlite/app/requirements.txt @@ -0,0 +1 @@ + diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-xenlite/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-xenlite/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..ea6305dbf390f75f34fead8fcaafeafb8e02feb1 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-xenlite/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-bioconductor-xenlite: + build: . + image: mcp-bioconductor-xenlite:latest + container_name: mcp-bioconductor-xenlite + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=bioconductor-xenlite + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-xenlite/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-xenlite/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..84fdb4485a585c36e86f691ff4826496f41992c8 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-xenlite/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - bioconductor-xenlite + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-xenlite/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-xenlite/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-xenlite/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-zlibbioc/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-zlibbioc/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..48bafb10c18bb5ffc2209a4f57ce2b58af3f38a3 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-zlibbioc/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install bioconductor-zlibbioc via conda (e.g., from bioconda) +RUN conda install -c bioconda bioconductor-zlibbioc -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY app/bioconductor-zlibbioc_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/bioconductor-zlibbioc_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/bioconductor-zlibbioc_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-zlibbioc/app/bioconductor-zlibbioc_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-zlibbioc/app/bioconductor-zlibbioc_server.py new file mode 100644 index 0000000000000000000000000000000000000000..d8cd26ea0a423357c44a614995cf6f3284f5c27d --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-zlibbioc/app/bioconductor-zlibbioc_server.py @@ -0,0 +1,145 @@ +import subprocess +from pathlib import Path +from typing import List, Optional, Dict, Any + +# No need to import mcp as per instructions + +@mcp.tool() +def bioconductor_zlibbioc_run_rscript( + r_script_file: Optional[Path] = None, + r_expressions: Optional[List[str]] = None, + r_args: Optional[List[str]] = None, + help_flag: bool = False, + version_flag: bool = False, + verbose: bool = False, + default_packages: Optional[str] = None, + save_workspace: bool = False, + no_environ: bool = False, + no_site_file: bool = False, + no_init_file: bool = False, + restore_workspace: bool = False, + vanilla: bool = False, +) -> Dict[str, Any]: + """ + Executes R scripts or expressions using the Rscript command-line utility. + + This tool provides a wrapper for `Rscript`, allowing users to run R code. + The `bioconductor-zlibbioc` package itself is an R package providing zlib + libraries and is not a standalone executable. Its functionality is accessed + via R code executed by `Rscript`. + + Note: The `bioconductor-zlibbioc` package is deprecated and has been removed + from Bioconductor. This tool wraps the general `Rscript` command, which can + be used to execute any R code, including attempts to install or use `zlibbioc` + if it were available. + + Args: + r_script_file: Path to an R script file to execute. Mutually exclusive with `r_expressions`. + The file must exist. + r_expressions: A list of R expressions (strings) to execute. Each string in the list + will be passed with a separate '-e' flag. Mutually exclusive with `r_script_file`. + r_args: Optional list of arguments to be passed to the R script or expressions. + These arguments are available within R via `commandArgs(trailingOnly=TRUE)`. + help_flag: If True, print usage information for Rscript and exit. + If set, other command-specific arguments will be ignored. + version_flag: If True, print version information for Rscript and exit. + If set, other command-specific arguments will be ignored. + verbose: If True, print information on Rscript's progress. + default_packages: A comma-separated string of package names, or 'NULL', + to specify default packages for the R session. + save_workspace: If True, save the R workspace at the end of the session. + no_environ: If True, do not read the site and user environment files. + no_site_file: If True, do not read the site-wide Rprofile. + no_init_file: If True, do not read the user R profile. + restore_workspace: If True, restore previously saved objects at startup. + vanilla: If True, combine `--no-save`, `--no-restore`, `--no-site-file`, + `--no-init-file`, and `--no-environ`. This option is mutually + exclusive with `save_workspace`, `no_environ`, `no_site_file`, + `no_init_file`, and `restore_workspace`. + + Returns: + A dictionary containing the command executed, stdout, stderr, and any output files. + """ + command = ["Rscript"] + output_files: List[Path] = [] + + # Handle --help and --version first as they typically cause Rscript to exit immediately + if help_flag: + command.append("--help") + elif version_flag: + command.append("--version") + else: + # Input validation for script file vs expressions + if r_script_file and r_expressions: + raise ValueError("Cannot specify both 'r_script_file' and 'r_expressions'. Choose one.") + if not r_script_file and not r_expressions: + raise ValueError("Must specify either 'r_script_file' or 'r_expressions' unless using --help or --version.") + + # Add R script file or expressions + if r_script_file: + if not r_script_file.is_file(): + raise FileNotFoundError(f"R script file not found: {r_script_file}") + command.append(str(r_script_file)) + elif r_expressions: + for expr in r_expressions: + command.extend(["-e", expr]) + + # Handle --vanilla and its conflicting options + if vanilla: + command.append("--vanilla") + if save_workspace or no_environ or no_site_file or no_init_file or restore_workspace: + raise ValueError( + "The '--vanilla' option is mutually exclusive with " + "'save_workspace', 'no_environ', 'no_site_file', 'no_init_file', and 'restore_workspace'." + ) + else: + # Add other Rscript options if --vanilla is not set + if verbose: + command.append("--verbose") + if default_packages is not None: + command.append(f"--default-packages={default_packages}") + if save_workspace: + command.append("--save") + if no_environ: + command.append("--no-environ") + if no_site_file: + command.append("--no-site-file") + if no_init_file: + command.append("--no-init-file") + if restore_workspace: + command.append("--restore") + + # Add arguments to be passed to the R script/expressions + if r_args: + command.extend(r_args) + + try: + process = subprocess.run( + command, + capture_output=True, + text=True, + check=True + ) + stdout = process.stdout + stderr = process.stderr + except FileNotFoundError: + return { + "command_executed": " ".join(command), + "stdout": "", + "stderr": "Error: Rscript command not found. Please ensure R is installed and in your system's PATH.", + "output_files": [], + } + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(e.cmd), + "stdout": e.stdout, + "stderr": e.stderr, + "output_files": [], + } + + return { + "command_executed": " ".join(command), + "stdout": stdout, + "stderr": stderr, + "output_files": output_files, + } \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-zlibbioc/app/bioconductor-zlibbioc_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-zlibbioc/app/bioconductor-zlibbioc_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..991a6ba7b9c151d56f03bacac3aa59279b3f5337 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-zlibbioc/app/bioconductor-zlibbioc_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-zlibbioc/app/bioconductor-zlibbioc_server.py') +SERVER_NAME = 'biosci_bioconductor_zlibbioc' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-zlibbioc/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-zlibbioc/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-zlibbioc/app/requirements.txt @@ -0,0 +1 @@ + diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-zlibbioc/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-zlibbioc/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..0725bb33611e65ca6c39a977c9733243aa80647c --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-zlibbioc/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-bioconductor-zlibbioc: + build: . + image: mcp-bioconductor-zlibbioc:latest + container_name: mcp-bioconductor-zlibbioc + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=bioconductor-zlibbioc + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-zlibbioc/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-zlibbioc/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..4d1768c84dd8493e0a4816be7680761fd5fb9a40 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-zlibbioc/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - bioconductor-zlibbioc + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-zlibbioc/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-zlibbioc/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_bioconductor-zlibbioc/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_c-ares/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_c-ares/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..c838dfe6abb8ea5aa3d5f168267e9f6a01cd4028 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_c-ares/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install c-ares via conda (e.g., from bioconda) +RUN conda install -c bioconda c-ares -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY app/c-ares_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/c-ares_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/c-ares_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_c-ares/app/c-ares_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_c-ares/app/c-ares_server.py new file mode 100644 index 0000000000000000000000000000000000000000..5104a7ed70e5adb85c3d9936b2d4c32689e58188 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_c-ares/app/c-ares_server.py @@ -0,0 +1,203 @@ +import subprocess +import logging +from typing import Optional, List + +# MCP-decorated functions for the c-ares command-line tools: adig and ahost + +@mcp.tool() +def adig( + hostname: str, + query_type: Optional[str] = None, + query_class: Optional[str] = None, + servers: Optional[List[str]] = None, + debug: bool = False, + use_tcp: bool = False, + use_udp: bool = False, + flags: Optional[List[str]] = None, + tcp_timeout: Optional[int] = None, + udp_timeout: Optional[int] = None, + retries: Optional[int] = None, +): + """ + Performs an asynchronous DNS lookup using the adig utility. + + adig is a command-line tool similar to dig that uses the c-ares library + for asynchronous DNS requests. + + Args: + hostname: The hostname to query. + query_type: The query type (e.g., A, AAAA, CNAME, MX, NS, PTR, SOA, SRV, TXT). Defaults to 'A'. + query_class: The query class (e.g., IN, CH, HS, ANY). Defaults to 'IN'. + servers: A list of DNS servers to use for the query. + debug: Enable debug output. + use_tcp: Use TCP for the query instead of UDP. + use_udp: Use UDP for the query (this is the default). + flags: A list of c-ares flags to set (e.g., 'usevc', 'norecurse'). + tcp_timeout: Set the TCP timeout in seconds. + udp_timeout: Set the UDP timeout in seconds. + retries: Set the number of retries. + """ + # Input validation + if use_tcp and use_udp: + raise ValueError("Cannot specify both use_tcp (-s) and use_udp (-u).") + + valid_flags = {"usevc", "primary", "igntc", "norecurse", "stayopen", "noaliases"} + if flags: + for flag in flags: + if flag not in valid_flags: + raise ValueError(f"Invalid flag '{flag}'. Valid flags are: {', '.join(valid_flags)}") + + if tcp_timeout is not None and tcp_timeout < 0: + raise ValueError("tcp_timeout must be a non-negative integer.") + + if udp_timeout is not None and udp_timeout < 0: + raise ValueError("udp_timeout must be a non-negative integer.") + + if retries is not None and retries < 0: + raise ValueError("retries must be a non-negative integer.") + + # Command construction + cmd = ["adig"] + + if debug: + cmd.append("-d") + if use_tcp: + cmd.append("-s") + if use_udp: + cmd.append("-u") + + if flags: + for flag in flags: + cmd.extend(["-f", flag]) + + if tcp_timeout is not None: + cmd.extend(["-T", str(tcp_timeout)]) + + if udp_timeout is not None: + cmd.extend(["-U", str(udp_timeout)]) + + if retries is not None: + cmd.extend(["-r", str(retries)]) + + if servers: + for server in servers: + cmd.append(f"@{server}") + + # Positional arguments + cmd.append(hostname) + if query_type: + cmd.append(query_type) + if query_class: + cmd.append(query_class) + + command_executed = " ".join(cmd) + logging.info(f"Executing command: {command_executed}") + + # Subprocess execution + try: + result = subprocess.run( + cmd, + capture_output=True, + text=True, + check=True + ) + return { + "command_executed": command_executed, + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": [] + } + except FileNotFoundError: + return { + "command_executed": command_executed, + "stdout": "", + "stderr": "Error: 'adig' command not found. Is c-ares installed and in the system's PATH?", + "output_files": [] + } + except subprocess.CalledProcessError as e: + logging.error(f"adig execution failed: {e.stderr}") + return { + "command_executed": command_executed, + "stdout": e.stdout, + "stderr": e.stderr, + "output_files": [] + } + +@mcp.tool() +def ahost( + host_or_address: str, + debug: bool = False, + flags: Optional[List[str]] = None, + family: Optional[str] = None, +): + """ + Performs an asynchronous DNS lookup for a hostname or IP address using the ahost utility. + + ahost is a command-line tool similar to host that uses the c-ares library + for asynchronous DNS requests. + + Args: + host_or_address: The hostname or IP address to look up. + debug: Enable debug output. + flags: A list of c-ares flags to set (e.g., 'usevc', 'norecurse'). + family: Address family to search for (one of 'inet', 'inet6'). + """ + # Input validation + valid_flags = {"usevc", "primary", "igntc", "norecurse", "stayopen", "noaliases"} + if flags: + for flag in flags: + if flag not in valid_flags: + raise ValueError(f"Invalid flag '{flag}'. Valid flags are: {', '.join(valid_flags)}") + + valid_families = {"inet", "inet6"} + if family and family not in valid_families: + raise ValueError(f"Invalid family '{family}'. Valid families are: 'inet', 'inet6'.") + + # Command construction + cmd = ["ahost"] + + if debug: + cmd.append("-d") + + if flags: + for flag in flags: + cmd.extend(["-f", flag]) + + if family: + cmd.extend(["-s", family]) + + # Positional argument + cmd.append(host_or_address) + + command_executed = " ".join(cmd) + logging.info(f"Executing command: {command_executed}") + + # Subprocess execution + try: + result = subprocess.run( + cmd, + capture_output=True, + text=True, + check=True + ) + return { + "command_executed": command_executed, + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": [] + } + except FileNotFoundError: + return { + "command_executed": command_executed, + "stdout": "", + "stderr": "Error: 'ahost' command not found. Is c-ares installed and in the system's PATH?", + "output_files": [] + } + except subprocess.CalledProcessError as e: + logging.error(f"ahost execution failed: {e.stderr}") + return { + "command_executed": command_executed, + "stdout": e.stdout, + "stderr": e.stderr, + "output_files": [] + } \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_c-ares/app/c-ares_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_c-ares/app/c-ares_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..5996ce42ae00829bd0932d4ed1470d92e3a9e24c --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_c-ares/app/c-ares_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_c-ares/app/c-ares_server.py') +SERVER_NAME = 'biosci_c_ares' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_c-ares/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_c-ares/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_c-ares/app/requirements.txt @@ -0,0 +1 @@ + diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_c-ares/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_c-ares/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..d40aed9ba9aa4997be56d2b66dc70055654731f9 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_c-ares/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-c-ares: + build: . + image: mcp-c-ares:latest + container_name: mcp-c-ares + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=c-ares + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_c-ares/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_c-ares/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..e37f8a3c620056dc5b7f9490d6e486291739c21d --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_c-ares/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - c-ares + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_c-ares/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_c-ares/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_c-ares/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_cellqc/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_cellqc/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..cf857a360bb89dc8448c35e587d8923712660aa2 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_cellqc/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install cellqc via conda (e.g., from bioconda) +RUN conda install -c bioconda cellqc -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY app/cellqc_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/cellqc_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/cellqc_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_cellqc/app/cellqc_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_cellqc/app/cellqc_server.py new file mode 100644 index 0000000000000000000000000000000000000000..da91d312e64afc13435adc98f74efafc702fb8eb --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_cellqc/app/cellqc_server.py @@ -0,0 +1,180 @@ +import subprocess +import tempfile +import yaml +import logging +from pathlib import Path +from typing import Optional, List, Dict + +# Set up logging +logging.basicConfig(level=logging.INFO) +logger = logging.getLogger(__name__) + +# MCP decorator is not defined here, but is expected to be in the execution environment. +# We will use a placeholder class for demonstration and type-hinting. +class mcp: + @staticmethod + def tool(func): + def wrapper(*args, **kwargs): + return func(*args, **kwargs) + return wrapper + +@mcp.tool +def run_cellqc( + samples_file: Path, + output_dir: Path, + numthreads: int = 12, + cbtag: str = "CB", + retag: str = "RE", + dropkick_skip: bool = False, + dropkick_method: str = "multiotsu", + dropkick_numthreads: int = 1, + filterbycount_mincount: int = 500, + filterbycount_minfeature: int = 300, + filterbycount_mito: int = 10, + doubletfinder_skip: bool = False, + doubletfinder_findpK: bool = False, + doubletfinder_numthreads: int = 5, + doubletfinder_pK: float = 0.01, + scpred_skip: bool = True, + scpred_reference: Optional[Path] = None, + scpred_threshold: float = 0.9, + dryrun: bool = False, +) -> Dict: + """ + Runs the cellqc standardized quality control pipeline for single-cell RNA-Seq data. + + This tool takes a sample sheet and various QC parameters to execute a Snakemake + workflow that filters empty droplets, removes doublets, and annotates cell types, + producing clean feature-count matrices. + """ + # 1. Input Validation + if not samples_file.is_file(): + raise FileNotFoundError(f"Sample file not found: {samples_file}") + + if not scpred_skip: + if scpred_reference is None: + raise ValueError("scpred.reference must be provided when scpred.skip is False.") + if not scpred_reference.is_file(): + raise FileNotFoundError(f"scPred reference file not found: {scpred_reference}") + + allowed_dropkick_methods = ["multiotsu", "otsu", "li", "mean"] + if dropkick_method not in allowed_dropkick_methods: + raise ValueError(f"dropkick.method must be one of {allowed_dropkick_methods}, not '{dropkick_method}'.") + + if numthreads <= 0: + raise ValueError("numthreads must be a positive integer.") + if filterbycount_mincount < 0 or filterbycount_minfeature < 0: + raise ValueError("mincount and minfeature must be non-negative.") + if not (0 <= filterbycount_mito <= 100): + raise ValueError("mito (mitochondria percentage) must be between 0 and 100.") + + # 2. Create Output Directory + output_dir.mkdir(parents=True, exist_ok=True) + + # 3. Generate YAML Config from parameters + config_data = { + "numthreads": numthreads, + "cbtag": cbtag, + "retag": retag, + "dropkick": { + "skip": dropkick_skip, + "method": dropkick_method, + "numthreads": dropkick_numthreads, + }, + "filterbycount": { + "mincount": filterbycount_mincount, + "minfeature": filterbycount_minfeature, + "mito": filterbycount_mito, + }, + "doubletfinder": { + "skip": doubletfinder_skip, + "findpK": doubletfinder_findpK, + "numthreads": doubletfinder_numthreads, + "pK": doubletfinder_pK, + }, + "scpred": { + "skip": scpred_skip, + "reference": str(scpred_reference) if scpred_reference else None, + "threshold": scpred_threshold, + }, + } + + # 4. Subprocess Execution + # The cellqc command-line interface is not explicitly documented. We assume a common + # pattern for Snakemake wrappers: passing sample/config files and core options as arguments. + # The wrapper is expected to handle the integration with the underlying Snakemake pipeline. + + command_executed = "" + stdout_str = "" + stderr_str = "" + output_files: List[str] = [] + + try: + with tempfile.NamedTemporaryFile(mode='w', suffix=".yaml", delete=False, dir=output_dir) as tmp_config: + yaml.dump(config_data, tmp_config, default_flow_style=False) + config_path = Path(tmp_config.name) + + # Base command + cmd = [ + "cellqc", + "--samples", str(samples_file.resolve()), + "--config", str(config_path.resolve()), + "--outdir", str(output_dir.resolve()), + "--cores", str(numthreads) + ] + + if dryrun: + cmd.append("--dryrun") + + command_executed = " ".join(cmd) + logger.info(f"Executing command: {command_executed}") + + process = subprocess.run( + cmd, + capture_output=True, + text=True, + check=True, + cwd=output_dir # Run in the output directory + ) + + stdout_str = process.stdout + stderr_str = process.stderr + + # 5. Collect Output Files + result_dir = output_dir / "result" + postproc_dir = output_dir / "postproc" + + if result_dir.is_dir(): + output_files.extend(str(p.resolve()) for p in result_dir.glob("*.h5seurat")) + output_files.extend(str(p.resolve()) for p in result_dir.glob("*.h5ad")) + if (result_dir / "report.html").is_file(): + output_files.append(str((result_dir / "report.html").resolve())) + + if postproc_dir.is_dir(): + output_files.extend(str(p.resolve()) for p in postproc_dir.glob("*.h5ad")) + + except FileNotFoundError: + raise RuntimeError("`cellqc` command not found. Please ensure it is installed and in your PATH.") + except subprocess.CalledProcessError as e: + logger.error(f"cellqc pipeline failed with exit code {e.returncode}") + logger.error(f"STDOUT: {e.stdout}") + logger.error(f"STDERR: {e.stderr}") + return { + "command_executed": command_executed, + "stdout": e.stdout, + "stderr": e.stderr, + "output_files": [], + "error": "Pipeline execution failed." + } + finally: + # Clean up the temporary config file + if 'config_path' in locals() and config_path.exists(): + config_path.unlink() + + # 6. Return Structured Output + return { + "command_executed": command_executed, + "stdout": stdout_str, + "stderr": stderr_str, + "output_files": output_files + } \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_cellqc/app/cellqc_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_cellqc/app/cellqc_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..eddd8c275ce39c47456602bfbc90a11b6053a031 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_cellqc/app/cellqc_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_cellqc/app/cellqc_server.py') +SERVER_NAME = 'biosci_cellqc' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_cellqc/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_cellqc/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..f62ce0c56db2b5d134cff98f7fb1625023cd2874 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_cellqc/app/requirements.txt @@ -0,0 +1 @@ +PyYAML==6.0.3 diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_cellqc/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_cellqc/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..041c8a5419777a6e83688b9368c771a416445f4e --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_cellqc/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-cellqc: + build: . + image: mcp-cellqc:latest + container_name: mcp-cellqc + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=cellqc + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_cellqc/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_cellqc/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..e255270dae33a2936ffcb379ad4835edf6a9aea7 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_cellqc/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - cellqc + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_cellqc/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_cellqc/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..c841f3ed8ad31753f4f5da0ef0b754abc017e725 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_cellqc/requirements.txt @@ -0,0 +1,3 @@ +PyYAML==6.0.3 +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_celltypist-so/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_celltypist-so/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..d7fa942a6b3140d1a6122745e70fd4748f6e0a40 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_celltypist-so/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install celltypist-so via conda (e.g., from bioconda) +RUN conda install -c bioconda celltypist-so -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY app/celltypist-so_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/celltypist-so_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/celltypist-so_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_celltypist-so/app/celltypist-so_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_celltypist-so/app/celltypist-so_server.py new file mode 100644 index 0000000000000000000000000000000000000000..9b4595041317dcc1fc4099ec085d9a06c6c14f46 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_celltypist-so/app/celltypist-so_server.py @@ -0,0 +1,259 @@ +import subprocess +import tempfile +from pathlib import Path +from typing import Optional, List + +# MCP decorator placeholder. +# In a real MCP environment, this would be provided by the MCP framework. +class mcp: + @staticmethod + def tool(): + def decorator(f): + return f + return decorator + +@mcp.tool() +def predict( + input_file: Path, + model: str = 'Immune_All_Low.pkl', + labels: Optional[Path] = None, + outdir: Optional[Path] = None, + prefix: Optional[str] = None, + transpose_input: bool = False, + gene_file: Optional[Path] = None, + cell_file: Optional[Path] = None, + feature_selection: bool = False, + majority_voting: bool = False, + over_clustering: Optional[str] = None, + min_prop: float = 0.0, + min_cells: int = 0, + p_val: float = 0.05, + plot_results: bool = False, + dot_plot: bool = False, + use_gpu: bool = False, + quiet: bool = False, +) -> dict: + """ + Run CellTypist for automated cell type annotation. + + This tool takes a count matrix as input and predicts cell types based on a pre-trained model. + It can also perform majority voting to refine predictions and generate plots for visualization. + """ + # Input validation + if not input_file.exists(): + raise FileNotFoundError(f"Input file not found: {input_file}") + if labels and not labels.exists(): + raise FileNotFoundError(f"Labels file not found: {labels}") + if gene_file and not gene_file.exists(): + raise FileNotFoundError(f"Gene file not found: {gene_file}") + if cell_file and not cell_file.exists(): + raise FileNotFoundError(f"Cell file not found: {cell_file}") + if majority_voting and not over_clustering: + raise ValueError("--over-clustering is required when --majority-voting is enabled.") + if not (0.0 <= min_prop <= 1.0): + raise ValueError("--min-prop must be between 0.0 and 1.0.") + if min_cells < 0: + raise ValueError("--min-cells must be a non-negative integer.") + if not (0.0 <= p_val <= 1.0): + raise ValueError("--p-val must be between 0.0 and 1.0.") + + with tempfile.TemporaryDirectory() as temp_dir: + work_dir = Path(outdir) if outdir else Path(temp_dir) + if outdir: + work_dir.mkdir(parents=True, exist_ok=True) + + cmd = [ + "celltypist", + str(input_file), + "--model", model, + "--outdir", str(work_dir) + ] + + if labels: + cmd.extend(["--labels", str(labels)]) + if prefix: + cmd.extend(["--prefix", prefix]) + if transpose_input: + cmd.append("--transpose-input") + if gene_file: + cmd.extend(["--gene-file", str(gene_file)]) + if cell_file: + cmd.extend(["--cell-file", str(cell_file)]) + if feature_selection: + cmd.append("--feature-selection") + if majority_voting: + cmd.append("--majority-voting") + if over_clustering: + cmd.extend(["--over-clustering", over_clustering]) + cmd.extend(["--min-prop", str(min_prop)]) + cmd.extend(["--min-cells", str(min_cells)]) + cmd.extend(["--p-val", str(p_val)]) + if plot_results: + cmd.append("--plot-results") + if dot_plot: + cmd.append("--dot-plot") + if use_gpu: + cmd.append("--use-gpu") + if quiet: + cmd.append("--quiet") + + try: + process = subprocess.run( + cmd, + check=True, + capture_output=True, + text=True, + ) + stdout = process.stdout + stderr = process.stderr + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(map(str, cmd)), + "stdout": e.stdout, + "stderr": e.stderr, + "return_code": e.returncode, + "output_files": [] + } + + # Discover output files + output_files: List[str] = [] + file_prefix = f"{prefix}" if prefix else "" + + # Main output files + predicted_labels_file = work_dir / f"{file_prefix}predicted_labels.csv" + if predicted_labels_file.exists(): + output_files.append(str(predicted_labels_file)) + + probability_matrix_file = work_dir / f"{file_prefix}probability_matrix.csv" + if probability_matrix_file.exists(): + output_files.append(str(probability_matrix_file)) + + # Optional plot files + if dot_plot: + dot_plot_file = work_dir / f"{file_prefix}dotplot.pdf" + if dot_plot_file.exists(): + output_files.append(str(dot_plot_file)) + + if plot_results: + plot_file_name = "celltypist_majority_voting_plot.pdf" if majority_voting else "celltypist_prediction_plot.pdf" + plot_file = work_dir / f"{file_prefix}{plot_file_name}" + if plot_file.exists(): + output_files.append(str(plot_file)) + + return { + "command_executed": " ".join(map(str, cmd)), + "stdout": stdout, + "stderr": stderr, + "output_files": output_files + } + +@mcp.tool() +def train( + matrix_file: Path, + labels_file: Path, + output_model_path: Path, + transpose_input: bool = False, + gene_file: Optional[Path] = None, + cell_file: Optional[Path] = None, + check_labels: bool = False, + feature_selection: bool = False, + n_features: int = 5000, + use_sgd: bool = False, + c: float = 1.0, + solver: str = 'liblinear', + penalty: str = 'l2', + max_iter: int = 200, + n_jobs: int = 1, + use_gpu: bool = False, + quiet: bool = False, +) -> dict: + """ + Train a CellTypist model from a count matrix and cell labels. + + This tool builds and trains a logistic regression or SGD classifier based on the provided + single-cell data, saving the resulting model to a file. + """ + # Input validation + if not matrix_file.exists(): + raise FileNotFoundError(f"Matrix file not found: {matrix_file}") + if not labels_file.exists(): + raise FileNotFoundError(f"Labels file not found: {labels_file}") + if gene_file and not gene_file.exists(): + raise FileNotFoundError(f"Gene file not found: {gene_file}") + if cell_file and not cell_file.exists(): + raise FileNotFoundError(f"Cell file not found: {cell_file}") + if n_features <= 0: + raise ValueError("--n-features must be a positive integer.") + if c <= 0: + raise ValueError("--C must be a positive float.") + if max_iter <= 0: + raise ValueError("--max-iter must be a positive integer.") + if n_jobs <= 0 and n_jobs != -1: + raise ValueError("--n-jobs must be a positive integer or -1.") + + # Ensure output directory exists + output_model_path.parent.mkdir(parents=True, exist_ok=True) + + cmd = [ + "celltypist", + "train", + "--matrix-file", str(matrix_file), + "--labels-file", str(labels_file), + str(output_model_path) + ] + + if transpose_input: + cmd.append("--transpose-input") + if gene_file: + cmd.extend(["--gene-file", str(gene_file)]) + if cell_file: + cmd.extend(["--cell-file", str(cell_file)]) + if check_labels: + cmd.append("--check-labels") + if feature_selection: + cmd.append("--feature-selection") + cmd.extend(["--n-features", str(n_features)]) + if use_sgd: + cmd.append("--use-SGD") + + # Logistic regression parameters (used if not --use-SGD) + cmd.extend(["--C", str(c)]) + cmd.extend(["--solver", solver]) + cmd.extend(["--penalty", penalty]) + cmd.extend(["--max-iter", str(max_iter)]) + + if n_jobs != 1: # Only add if not default + cmd.extend(["--n-jobs", str(n_jobs)]) + if use_gpu: + cmd.append("--use-gpu") + if quiet: + cmd.append("--quiet") + + try: + process = subprocess.run( + cmd, + check=True, + capture_output=True, + text=True, + ) + stdout = process.stdout + stderr = process.stderr + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(map(str, cmd)), + "stdout": e.stdout, + "stderr": e.stderr, + "return_code": e.returncode, + "output_files": [] + } + + output_files: List[str] = [] + if output_model_path.exists(): + output_files.append(str(output_model_path)) + + return { + "command_executed": " ".join(map(str, cmd)), + "stdout": stdout, + "stderr": stderr, + "output_files": output_files + } \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_celltypist-so/app/celltypist-so_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_celltypist-so/app/celltypist-so_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..f07545eb5424bbdc844089f55087386a43008671 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_celltypist-so/app/celltypist-so_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_celltypist-so/app/celltypist-so_server.py') +SERVER_NAME = 'biosci_celltypist_so' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_celltypist-so/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_celltypist-so/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_celltypist-so/app/requirements.txt @@ -0,0 +1 @@ + diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_celltypist-so/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_celltypist-so/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..84733e7535686820461af2189f59c50748c5e04d --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_celltypist-so/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-celltypist-so: + build: . + image: mcp-celltypist-so:latest + container_name: mcp-celltypist-so + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=celltypist-so + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_celltypist-so/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_celltypist-so/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..36a63a888a35759b3a2e84759d190e09f6e2b36b --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_celltypist-so/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - celltypist-so + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_celltypist-so/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_celltypist-so/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_celltypist-so/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_constellations/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_constellations/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..f69e44d668aedbe7dbc4df45f73ce70f647ad9fd --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_constellations/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install constellations via conda (e.g., from bioconda) +RUN conda install -c bioconda constellations -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY app/constellations_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/constellations_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/constellations_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_constellations/app/constellations_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_constellations/app/constellations_server.py new file mode 100644 index 0000000000000000000000000000000000000000..52447a756e68bb1791fbf8632d531f261f5cc920 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_constellations/app/constellations_server.py @@ -0,0 +1,92 @@ +import subprocess +import json +from pathlib import Path +from typing import Optional + +# MCP-ready code for the 'constellations' package. +# Based on the analysis, 'constellations' is a data package providing JSON definitions +# for SARS-CoV-2 mutations, intended for use by other tools like 'scorpio' and 'pangolin'. +# It does not have a command-line interface of its own. The error message from the +# --help attempt confirms this. +# +# Therefore, instead of wrapping a non-existent CLI, this MCP tool will provide +# information about the package and how to access its data, which is the primary +# function of this package. + +@mcp.tool() +def constellations_info(): + """ + Provides information about the constellations data package. + + The 'constellations' package is a data-only package containing descriptions of + constellations of mutations for the SARS-CoV-2 virus. It does not provide a + command-line executable. Instead, it installs a collection of JSON files used by + other tools like 'scorpio' and 'pangolin' to classify viral lineages. + + This function returns the path to the installed constellations data directory. + """ + # The 'constellations' package does not have a CLI. + # The command `constellations --help` fails with a ModuleNotFoundError. + # This function will instead provide information about the package's purpose + # and locate the data files it provides. + + command_to_find_path = [ + "python", + "-c", + "import constellations; import os; print(os.path.dirname(constellations.__file__))" + ] + + command_executed = " ".join(command_to_find_path) + stdout_message = "" + stderr_message = "" + output_files = {} + + try: + # Execute a python command to find the installation path of the package + process = subprocess.run( + command_to_find_path, + capture_output=True, + text=True, + check=True + ) + + package_path = Path(process.stdout.strip()) + constellations_dir = package_path / "constellations" + + if not constellations_dir.is_dir(): + raise FileNotFoundError(f"Constellations data directory not found at: {constellations_dir}") + + # List the contents of the constellations directory + files = [str(f) for f in constellations_dir.iterdir() if f.is_file()] + + stdout_message = ( + "The 'constellations' package is a data package for SARS-CoV-2 mutations.\n" + "It does not have a direct command-line interface.\n" + "The data files are located in the directory listed in 'output_files'.\n" + f"Found {len(files)} definition files." + ) + + output_files = { + "constellations_data_directory": str(constellations_dir), + "definition_files": files + } + + stderr_message = process.stderr + + except FileNotFoundError as e: + stderr_message = str(e) + stdout_message = "Could not locate the constellations data directory. The package might not be installed correctly." + except subprocess.CalledProcessError as e: + stdout_message = "Failed to locate the constellations package path." + stderr_message = e.stderr + except Exception as e: + stdout_message = "An unexpected error occurred." + stderr_message = str(e) + + + return { + "command_executed": command_executed, + "stdout": stdout_message, + "stderr": stderr_message, + "output_files": output_files + } diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_constellations/app/constellations_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_constellations/app/constellations_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..71f163ad71dfb3f3ac6de04fa4b663ca234f28e1 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_constellations/app/constellations_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_constellations/app/constellations_server.py') +SERVER_NAME = 'biosci_constellations' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_constellations/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_constellations/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_constellations/app/requirements.txt @@ -0,0 +1 @@ + diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_constellations/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_constellations/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..d0d68dfd5d9d4689f1674199d184eebf729b1803 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_constellations/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-constellations: + build: . + image: mcp-constellations:latest + container_name: mcp-constellations + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=constellations + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_constellations/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_constellations/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..8c02af7b6017bcad5d5bed0e888d7c9dd3fe573c --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_constellations/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - constellations + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_constellations/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_constellations/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_constellations/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_cooler/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_cooler/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..25e4076bfdf8bf91b84245143a37fdfb950645e6 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_cooler/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install cooler via conda (e.g., from bioconda) +RUN conda install -c bioconda cooler -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY app/cooler_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/cooler_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/cooler_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_cooler/app/cooler_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_cooler/app/cooler_server.py new file mode 100644 index 0000000000000000000000000000000000000000..8ad77c822e93f06b03dfb95c0e929a94586af71c --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_cooler/app/cooler_server.py @@ -0,0 +1,730 @@ +import subprocess +import tempfile +from pathlib import Path +from typing import Optional, List, Dict, Any + +# Helper function to handle subprocess execution and error reporting +def _run_cooler_command(cmd: List[str], output_path: Optional[Path] = None) -> Dict[str, Any]: + """ + Executes a cooler command using subprocess, captures output, and handles errors. + + Args: + cmd: A list of strings representing the command and its arguments. + output_path: The path to the primary output file, if one is created. + + Returns: + A dictionary containing the execution details and results. + """ + command_str = " ".join(cmd) + try: + result = subprocess.run( + cmd, + capture_output=True, + text=True, + check=True, + ) + output_files = [str(output_path)] if output_path else [] + # If output is redirected to stdout and no file is created + if not output_files and result.stdout: + # Create a temporary file to store stdout if it's substantial + if len(result.stdout) > 1024: # Heuristic for large output + with tempfile.NamedTemporaryFile(mode='w', delete=False, suffix='.txt', prefix='cooler_stdout_') as tmp: + tmp.write(result.stdout) + output_files.append(tmp.name) + + return { + "command_executed": command_str, + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": output_files, + } + except FileNotFoundError: + return { + "command_executed": command_str, + "stdout": "", + "stderr": "Error: 'cooler' command not found. Please ensure it is installed and in your PATH.", + "output_files": [], + "error": "Command not found", + } + except subprocess.CalledProcessError as e: + return { + "command_executed": command_str, + "stdout": e.stdout, + "stderr": e.stderr, + "output_files": [], + "error": "Command failed with exit code {}".format(e.returncode), + } + +@mcp.tool() +def cooler_cload_tabix( + bins_path: Path, + pairs_path: Path, + cool_path: Path, + format_str: str = "COO", + metadata: Optional[Path] = None, + assembly: Optional[str] = None, + chunksize: int = 1000000, + zero_based: bool = False, + field: Optional[List[str]] = None, + count_as_float: bool = False, + append: bool = False, + no_symmetric_upper: bool = False, +) -> Dict[str, Any]: + """ + Load a contact matrix from a tabix-indexed pairs file. + + Args: + bins_path: Path to a BED-like file containing genomic bins. + pairs_path: Path to a sorted, bgzipped and tabix-indexed contact list file. + cool_path: Output cooler path. + format_str: Storage format for pixels. One of 'COO', 'CSR', 'Dense'. + metadata: Path to a JSON file containing user-defined metadata. + assembly: Name of genome assembly. + chunksize: The number of pixels loaded in each chunk. + zero_based: If set, coordinates in the contact list are 0-based. + field: Specify which columns to load and how, e.g., 'count=4:dtype=int'. Can be used multiple times. + count_as_float: Store the 'count' column as a float. + append: Append to an existing cooler file. + no_symmetric_upper: Create a non-symmetric cooler. + """ + if not bins_path.is_file(): + raise FileNotFoundError(f"Bins file not found: {bins_path}") + if not pairs_path.is_file(): + raise FileNotFoundError(f"Pairs file not found: {pairs_path}") + if format_str not in ["COO", "CSR", "Dense"]: + raise ValueError("format_str must be one of 'COO', 'CSR', 'Dense'") + + cmd = [ + "cooler", "cload", "tabix", + str(bins_path), + str(pairs_path), + str(cool_path), + ] + cmd.extend(["--format", format_str]) + if metadata: + if not metadata.is_file(): + raise FileNotFoundError(f"Metadata file not found: {metadata}") + cmd.extend(["--metadata", str(metadata)]) + if assembly: + cmd.extend(["--assembly", assembly]) + cmd.extend(["--chunksize", str(chunksize)]) + if zero_based: + cmd.append("--zero-based") + if field: + for f in field: + cmd.extend(["--field", f]) + if count_as_float: + cmd.append("--count-as-float") + if append: + cmd.append("--append") + if no_symmetric_upper: + cmd.append("--no-symmetric-upper") + + return _run_cooler_command(cmd, cool_path) + +@mcp.tool() +def cooler_cload_pairix( + bins_path: Path, + pairs_path: Path, + cool_path: Path, + metadata: Optional[Path] = None, + assembly: Optional[str] = None, + chunksize: int = 1000000, + zero_based: bool = False, + field: Optional[List[str]] = None, + count_as_float: bool = False, + append: bool = False, + no_symmetric_upper: bool = False, +) -> Dict[str, Any]: + """ + Load a contact matrix from a pairix-indexed pairs file. + + Args: + bins_path: Path to a BED-like file containing genomic bins. + pairs_path: Path to a sorted, bgzipped and pairix-indexed contact list file. + cool_path: Output cooler path. + metadata: Path to a JSON file containing user-defined metadata. + assembly: Name of genome assembly. + chunksize: The number of pixels loaded in each chunk. + zero_based: If set, coordinates in the contact list are 0-based. + field: Specify which columns to load and how, e.g., 'count=4:dtype=int'. Can be used multiple times. + count_as_float: Store the 'count' column as a float. + append: Append to an existing cooler file. + no_symmetric_upper: Create a non-symmetric cooler. + """ + if not bins_path.is_file(): + raise FileNotFoundError(f"Bins file not found: {bins_path}") + if not pairs_path.is_file(): + raise FileNotFoundError(f"Pairs file not found: {pairs_path}") + + cmd = [ + "cooler", "cload", "pairix", + str(bins_path), + str(pairs_path), + str(cool_path), + ] + if metadata: + if not metadata.is_file(): + raise FileNotFoundError(f"Metadata file not found: {metadata}") + cmd.extend(["--metadata", str(metadata)]) + if assembly: + cmd.extend(["--assembly", assembly]) + cmd.extend(["--chunksize", str(chunksize)]) + if zero_based: + cmd.append("--zero-based") + if field: + for f in field: + cmd.extend(["--field", f]) + if count_as_float: + cmd.append("--count-as-float") + if append: + cmd.append("--append") + if no_symmetric_upper: + cmd.append("--no-symmetric-upper") + + return _run_cooler_command(cmd, cool_path) + +@mcp.tool() +def cooler_csort( + pairs_path: Path, + output_file: Optional[Path] = None, + nproc: int = 8, + chunksize: int = 5000000, + tmp_dir: Optional[Path] = None, + memory: str = "4G", +) -> Dict[str, Any]: + """ + Sort a contact list file (pairs format). + + Args: + pairs_path: Path to a pairs file. Use '-' for stdin. + output_file: Output file path. If not specified, outputs to stdout. + nproc: Number of processes to use for sorting. + chunksize: Number of lines to process per chunk. + tmp_dir: Temporary directory for sorting. + memory: Maximum memory to use for sorting, e.g., '10G'. + """ + if str(pairs_path) != "-" and not pairs_path.is_file(): + raise FileNotFoundError(f"Pairs file not found: {pairs_path}") + + cmd = ["cooler", "csort", str(pairs_path)] + if output_file: + cmd.extend(["-o", str(output_file)]) + cmd.extend(["-p", str(nproc)]) + cmd.extend(["-c", str(chunksize)]) + if tmp_dir: + tmp_dir.mkdir(parents=True, exist_ok=True) + cmd.extend(["-T", str(tmp_dir)]) + cmd.extend(["-M", memory]) + + return _run_cooler_command(cmd, output_file) + +@mcp.tool() +def cooler_load( + bins_path: Path, + pixels_path: Path, + cool_path: Path, + format_str: str = "coo", + metadata: Optional[Path] = None, + assembly: Optional[str] = None, + chunksize: int = 1000000, + field: Optional[List[str]] = None, + count_as_float: bool = False, + no_symmetric_upper: bool = False, +) -> Dict[str, Any]: + """ + Load a contact matrix from a tabular file. + + Args: + bins_path: Path to a BED-like file containing genomic bins. + pixels_path: Path to a file containing pixel data. + cool_path: Output cooler path. + format_str: Format of the pixel file. One of 'coo' or 'bg2'. + metadata: Path to a JSON file containing user-defined metadata. + assembly: Name of genome assembly. + chunksize: The number of pixels loaded in each chunk. + field: Specify which columns to load and how. + count_as_float: Store the 'count' column as a float. + no_symmetric_upper: Create a non-symmetric cooler. + """ + if not bins_path.is_file(): + raise FileNotFoundError(f"Bins file not found: {bins_path}") + if not pixels_path.is_file(): + raise FileNotFoundError(f"Pixels file not found: {pixels_path}") + if format_str not in ["coo", "bg2"]: + raise ValueError("format_str must be one of 'coo' or 'bg2'") + + cmd = ["cooler", "load", str(bins_path), str(pixels_path), str(cool_path)] + cmd.extend(["--format", format_str]) + if metadata: + if not metadata.is_file(): + raise FileNotFoundError(f"Metadata file not found: {metadata}") + cmd.extend(["--metadata", str(metadata)]) + if assembly: + cmd.extend(["--assembly", assembly]) + cmd.extend(["--chunksize", str(chunksize)]) + if field: + for f in field: + cmd.extend(["--field", f]) + if count_as_float: + cmd.append("--count-as-float") + if no_symmetric_upper: + cmd.append("--no-symmetric-upper") + + return _run_cooler_command(cmd, cool_path) + +@mcp.tool() +def cooler_merge( + cool_path: Path, + in_paths: List[Path], + chunksize: int = 1000000, + merge_attrs: str = "union", +) -> Dict[str, Any]: + """ + Merge multiple cooler files. + + Args: + cool_path: Output cooler path. + in_paths: List of at least two input cooler paths. + chunksize: The number of pixels loaded in each chunk. + merge_attrs: Strategy for merging attributes. One of 'union', 'intersection', 'first', 'last', 'unique', 'fail'. + """ + if len(in_paths) < 2: + raise ValueError("At least two input cooler paths are required for merging.") + for p in in_paths: + if not p.is_file(): + raise FileNotFoundError(f"Input cooler file not found: {p}") + if merge_attrs not in ["union", "intersection", "first", "last", "unique", "fail"]: + raise ValueError(f"Invalid merge_attrs strategy: {merge_attrs}") + + cmd = ["cooler", "merge", str(cool_path)] + cmd.extend([str(p) for p in in_paths]) + cmd.extend(["--chunksize", str(chunksize)]) + cmd.extend(["--merge-attrs", merge_attrs]) + + return _run_cooler_command(cmd, cool_path) + +@mcp.tool() +def cooler_coarsen( + cool_path: Path, + factor: int, + output_file: Optional[Path] = None, + nproc: int = 8, + chunksize: int = 1000000, + agg: str = "sum", +) -> Dict[str, Any]: + """ + Coarsen a cooler to a lower resolution. + + Args: + cool_path: Path to a cooler file. + factor: The coarsening factor. + output_file: Output file path. If not specified, appends to the input file. + nproc: Number of processes to use. + chunksize: Number of pixels to process per chunk. + agg: Aggregation function for value columns. Default is 'sum'. + """ + if not cool_path.is_file(): + raise FileNotFoundError(f"Input cooler file not found: {cool_path}") + if factor <= 0: + raise ValueError("Factor must be a positive integer.") + + cmd = ["cooler", "coarsen", str(cool_path)] + cmd.extend(["-k", str(factor)]) + if output_file: + cmd.extend(["-o", str(output_file)]) + cmd.extend(["-p", str(nproc)]) + cmd.extend(["--chunksize", str(chunksize)]) + cmd.extend(["--agg", agg]) + + return _run_cooler_command(cmd, output_file if output_file else cool_path) + +@mcp.tool() +def cooler_zoomify( + cool_path: Path, + output_file: Optional[Path] = None, + resolutions: Optional[str] = None, + nproc: int = 1, + chunksize: int = 1000000, + balance: bool = False, + balance_args: Optional[str] = None, + legacy: bool = False, +) -> Dict[str, Any]: + """ + Generate a multi-resolution cooler file. + + Args: + cool_path: Path to a cooler file. + output_file: Output file path. If not specified, creates a new file with a .mcool extension. + resolutions: Comma-separated list of target resolutions. + nproc: Number of processes to use. + chunksize: The number of pixels loaded in each chunk. + balance: Apply balancing to each resolution. + balance_args: Arguments to pass to the balancing function. + legacy: Use the legacy layout for the multi-resolution file. + """ + if not cool_path.is_file(): + raise FileNotFoundError(f"Input cooler file not found: {cool_path}") + + cmd = ["cooler", "zoomify", str(cool_path)] + if output_file: + cmd.extend(["-o", str(output_file)]) + if resolutions: + cmd.extend(["--resolutions", resolutions]) + cmd.extend(["-p", str(nproc)]) + cmd.extend(["--chunksize", str(chunksize)]) + if balance: + cmd.append("--balance") + if balance_args: + cmd.extend(["--balance-args", balance_args]) + if legacy: + cmd.append("--legacy") + + out_path = output_file if output_file else Path(str(cool_path).replace(".cool", ".mcool")) + return _run_cooler_command(cmd, out_path) + +@mcp.tool() +def cooler_balance( + cool_path: Path, + nproc: int = 8, + chunksize: int = 1000000, + mad_max: int = 5, + min_nnz: int = 10, + min_count: int = 0, + ignore_diags: int = 2, + tol: float = 1e-05, + max_iters: int = 200, + cis_only: bool = False, + trans_only: bool = False, + name: str = "weight", + force: bool = False, + check: bool = False, + append: bool = False, + stdout: bool = False, +) -> Dict[str, Any]: + """ + Balance a cooler matrix using iterative correction. + + Args: + cool_path: Path to a cooler file. + nproc: Number of processes to use. + chunksize: Number of pixels to process per chunk. + mad_max: Filter out bins with MAD > mad_max. + min_nnz: Filter out bins with < min_nnz contacts. + min_count: Filter out bins with < min_count contacts. + ignore_diags: Number of diagonals to ignore. + tol: Tolerance for convergence. + max_iters: Maximum number of iterations. + cis_only: Only balance cis data. + trans_only: Only balance trans data. + name: Name of the weight column to create. + force: Overwrite existing weights. + check: Check if the matrix is balanced. + append: Append weights to the file instead of overwriting. + stdout: Print weights to stdout instead of writing to the file. + """ + if not cool_path.is_file(): + raise FileNotFoundError(f"Input cooler file not found: {cool_path}") + if cis_only and trans_only: + raise ValueError("Cannot specify both --cis-only and --trans-only.") + + cmd = ["cooler", "balance", str(cool_path)] + cmd.extend(["-p", str(nproc)]) + cmd.extend(["--chunksize", str(chunksize)]) + cmd.extend(["--mad-max", str(mad_max)]) + cmd.extend(["--min-nnz", str(min_nnz)]) + cmd.extend(["--min-count", str(min_count)]) + cmd.extend(["--ignore-diags", str(ignore_diags)]) + cmd.extend(["--tol", str(tol)]) + cmd.extend(["--max-iters", str(max_iters)]) + if cis_only: + cmd.append("--cis-only") + if trans_only: + cmd.append("--trans-only") + cmd.extend(["--name", name]) + if force: + cmd.append("--force") + if check: + cmd.append("--check") + if append: + cmd.append("--append") + if stdout: + cmd.append("--stdout") + + # If not writing to stdout, the command modifies the input file. + output_file = None if stdout else cool_path + return _run_cooler_command(cmd, output_file) + +@mcp.tool() +def cooler_dump( + cool_path: Path, + output_file: Optional[Path] = None, + table: str = "pixels", + header: bool = False, + columns: Optional[str] = None, + range_str: Optional[str] = None, + range2_str: Optional[str] = None, + fill_lower: Optional[str] = None, + join: bool = False, + annotate: Optional[str] = None, + chunksize: Optional[int] = None, + one_based_starts: bool = False, + na_rep: str = "nan", +) -> Dict[str, Any]: + """ + Dump data from a cooler file to a text stream. + + Args: + cool_path: Path to a cooler file. + output_file: Output file path. If not specified, outputs to stdout. + table: Which table to dump: 'chroms', 'bins', 'pixels', or 'indexes'. + header: Print the header. + columns: Comma-separated list of columns to print. + range_str: Genomic range, e.g., 'chr1:1000-2000'. + range2_str: Second genomic range for cis/trans queries. + fill_lower: How to fill the lower triangle: 'zero' or 'copy'. + join: Join with bin coordinates (for pixels table). + annotate: Annotate bins with a BED-like file. Format: '[FIELD=]PATH[:COLUMN]'. + chunksize: Number of rows to dump at a time. + one_based_starts: Use 1-based starts in output. + na_rep: String representation for missing values. + """ + if not cool_path.is_file(): + raise FileNotFoundError(f"Input cooler file not found: {cool_path}") + if table not in ["chroms", "bins", "pixels", "indexes"]: + raise ValueError("table must be one of 'chroms', 'bins', 'pixels', 'indexes'") + if fill_lower and fill_lower not in ["zero", "copy"]: + raise ValueError("fill_lower must be one of 'zero' or 'copy'") + + cmd = ["cooler", "dump", str(cool_path)] + if output_file: + cmd.extend(["-o", str(output_file)]) + cmd.extend(["--table", table]) + if header: + cmd.append("--header") + if columns: + cmd.extend(["--columns", columns]) + if range_str: + cmd.extend(["--range", range_str]) + if range2_str: + cmd.extend(["--range2", range2_str]) + if fill_lower: + cmd.extend(["--fill-lower", fill_lower]) + if join: + cmd.append("--join") + if annotate: + cmd.extend(["--annotate", annotate]) + if chunksize: + cmd.extend(["--chunksize", str(chunksize)]) + if one_based_starts: + cmd.append("--one-based-starts") + cmd.extend(["--na-rep", na_rep]) + + return _run_cooler_command(cmd, output_file) + +@mcp.tool() +def cooler_show( + cool_path: Path, + range_str: str, + output_file: Optional[Path] = None, + range2_str: Optional[str] = None, + dpi: int = 100, + cmap: str = "YlOrRd", + zmin: Optional[float] = None, + zmax: Optional[float] = None, + vmin: Optional[float] = None, + vmax: Optional[float] = None, + norm: str = "log", + balance_weight: Optional[str] = "weight", + as_pixels: bool = False, + max_size: int = 500, +) -> Dict[str, Any]: + """ + Show a cooler's pixels as a matrix image. + + Args: + cool_path: Path to a cooler file. + range_str: Genomic range to display. + output_file: Output image file path. If not specified, shows interactively (not supported in MCP). + range2_str: Second genomic range for trans views. + dpi: Dots per inch for the output image. + cmap: Colormap for the matrix. + zmin: Minimum of the color scale (before norm). + zmax: Maximum of the color scale (before norm). + vmin: Minimum of the color scale (after norm). + vmax: Maximum of the color scale (after norm). + norm: Normalization for the color scale: 'log' or 'linear'. + balance_weight: Name of the balancing weight column to use. Set to None to disable balancing. + as_pixels: Show individual pixels instead of a heatmap. + max_size: Maximum size of the matrix to display. + """ + if not cool_path.is_file(): + raise FileNotFoundError(f"Input cooler file not found: {cool_path}") + if norm not in ["log", "linear"]: + raise ValueError("norm must be 'log' or 'linear'") + if not output_file: + raise ValueError("output_file must be specified for non-interactive use.") + + cmd = ["cooler", "show", str(cool_path), range_str] + cmd.extend(["-o", str(output_file)]) + if range2_str: + cmd.extend(["--range2", range2_str]) + cmd.extend(["--dpi", str(dpi)]) + cmd.extend(["--cmap", cmap]) + if zmin is not None: + cmd.extend(["--zmin", str(zmin)]) + if zmax is not None: + cmd.extend(["--zmax", str(zmax)]) + if vmin is not None: + cmd.extend(["--vmin", str(vmin)]) + if vmax is not None: + cmd.extend(["--vmax", str(vmax)]) + cmd.extend(["--norm", norm]) + if balance_weight: + cmd.extend(["--balance", balance_weight]) + else: + cmd.append("--no-balance") + if as_pixels: + cmd.append("--as-pixels") + cmd.extend(["--max-size", str(max_size)]) + + return _run_cooler_command(cmd, output_file) + +@mcp.tool() +def cooler_info( + cool_path: Path, + field: Optional[str] = None, + metadata: bool = False, + output_file: Optional[Path] = None, +) -> Dict[str, Any]: + """ + Display information about a cooler file. + + Args: + cool_path: Path to a cooler file. + field: A specific field to retrieve from the info dictionary. + metadata: Show the full metadata dictionary. + output_file: Output file path. If not specified, outputs to stdout. + """ + if not cool_path.is_file(): + raise FileNotFoundError(f"Input cooler file not found: {cool_path}") + + cmd = ["cooler", "info", str(cool_path)] + if field: + cmd.extend(["--field", field]) + if metadata: + cmd.append("--metadata") + if output_file: + cmd.extend(["-o", str(output_file)]) + + return _run_cooler_command(cmd, output_file) + +@mcp.tool() +def cooler_list( + cool_path: Path, + long_format: bool = False, +) -> Dict[str, Any]: + """ + List the contents of a cooler file. + + Args: + cool_path: Path to a cooler file (cool or mcool). + long_format: Use long listing format. + """ + if not cool_path.is_file(): + raise FileNotFoundError(f"Input cooler file not found: {cool_path}") + + cmd = ["cooler", "list", str(cool_path)] + if long_format: + cmd.append("-l") + + return _run_cooler_command(cmd) + +@mcp.tool() +def cooler_copy( + uri1: str, + uri2: str, + chunksize: int = 1000000, + append: bool = False, + no_index: bool = False, +) -> Dict[str, Any]: + """ + Copy a cooler from one file to another. + + Args: + uri1: Source cooler URI, e.g., 'path/to/file.cool::/path/inside'. + uri2: Destination cooler URI. + chunksize: The number of pixels loaded in each chunk. + append: Append to an existing cooler file. + no_index: Do not copy the bin-offset index. + """ + # Basic validation for file paths in URIs + src_path_str = uri1.split('::')[0] + if not Path(src_path_str).is_file(): + raise FileNotFoundError(f"Source cooler file not found: {src_path_str}") + + cmd = ["cooler", "copy", uri1, uri2] + cmd.extend(["--chunksize", str(chunksize)]) + if append: + cmd.append("--append") + if no_index: + cmd.append("--no-index") + + dest_path = Path(uri2.split('::')[0]) + return _run_cooler_command(cmd, dest_path) + +@mcp.tool() +def cooler_makebins( + chromsizes_path: Path, + binsize: int, + output_file: Optional[Path] = None, + id_decorator: Optional[str] = None, + name_col: Optional[str] = None, +) -> Dict[str, Any]: + """ + Create a genome-wide bin segmentation. + + Args: + chromsizes_path: Path to a chromsizes file. + binsize: The bin size in base pairs. + output_file: Output file path. If not specified, outputs to stdout. + id_decorator: A format string to generate bin IDs. + name_col: Name of the column for bin names. + """ + if not chromsizes_path.is_file(): + raise FileNotFoundError(f"Chromsizes file not found: {chromsizes_path}") + if binsize <= 0: + raise ValueError("Bin size must be a positive integer.") + + cmd = ["cooler", "makebins", str(chromsizes_path), str(binsize)] + if output_file: + cmd.extend(["-o", str(output_file)]) + if id_decorator: + cmd.extend(["--id-decorator", id_decorator]) + if name_col: + cmd.extend(["--name-col", name_col]) + + return _run_cooler_command(cmd, output_file) + +@mcp.tool() +def cooler_digest( + chromsizes_path: Path, + enzyme: str, + output_file: Optional[Path] = None, +) -> Dict[str, Any]: + """ + Create a genome-wide bin segmentation from restriction enzyme digestion. + + Args: + chromsizes_path: Path to a chromsizes file. + enzyme: Name of the restriction enzyme. + output_file: Output file path. If not specified, outputs to stdout. + """ + if not chromsizes_path.is_file(): + raise FileNotFoundError(f"Chromsizes file not found: {chromsizes_path}") + + cmd = ["cooler", "digest", str(chromsizes_path), enzyme] + if output_file: + cmd.extend(["-o", str(output_file)]) + + return _run_cooler_command(cmd, output_file) \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_cooler/app/cooler_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_cooler/app/cooler_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..24f093a697aaabac5b942b96c18221c4d83329f7 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_cooler/app/cooler_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_cooler/app/cooler_server.py') +SERVER_NAME = 'biosci_cooler' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_cooler/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_cooler/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_cooler/app/requirements.txt @@ -0,0 +1 @@ + diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_cooler/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_cooler/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..69eeb0dadc0c2736f9f5ae6a799b997c929055f5 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_cooler/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-cooler: + build: . + image: mcp-cooler:latest + container_name: mcp-cooler + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=cooler + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_cooler/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_cooler/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..06b366c07467542c5bfec00ec6ab9385dcee9ef0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_cooler/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - cooler + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_cooler/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_cooler/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_cooler/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_coreutils/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_coreutils/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..3beef9d62cb9bd80555a8e6d2f242dd2c94ebd84 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_coreutils/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install coreutils via conda (e.g., from bioconda) +RUN conda install -c bioconda coreutils -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY app/coreutils_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/coreutils_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/coreutils_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_coreutils/app/coreutils_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_coreutils/app/coreutils_server.py new file mode 100644 index 0000000000000000000000000000000000000000..51184a06440bd5d0ffc321c6af285dcaabfe5bf6 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_coreutils/app/coreutils_server.py @@ -0,0 +1,491 @@ +import subprocess +import tempfile +from pathlib import Path +from typing import List, Optional, Dict, Any + +# MCP-ready implementations for a selection of GNU Core Utilities. +# Note: coreutils is a suite of many individual tools. This file provides +# MCP wrappers for a commonly used subset of those tools. + +def _run_command(command: List[str], output_file: Optional[Path] = None) -> Dict[str, Any]: + """Helper function to run a command and handle output.""" + try: + if output_file: + with open(output_file, "w") as f: + result = subprocess.run( + command, + check=True, + capture_output=True, + text=True, + stdout=f + ) + # Read back a snippet of the output file for the stdout key + # to avoid returning massive amounts of data. + with open(output_file, "r") as f: + stdout_snippet = f.read(1024) + if len(stdout_snippet) == 1024: + stdout_snippet += "\n... (output truncated)" + return { + "command_executed": " ".join(command), + "stdout": stdout_snippet, + "stderr": result.stderr, + "output_files": [str(output_file)] + } + else: + result = subprocess.run( + command, + check=True, + capture_output=True, + text=True + ) + return { + "command_executed": " ".join(command), + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": [] + } + except FileNotFoundError: + return { + "command_executed": " ".join(command), + "stdout": "", + "stderr": f"Error: The command '{command[0]}' was not found. Ensure coreutils is in the system's PATH.", + "output_files": [] + } + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(command), + "stdout": e.stdout, + "stderr": e.stderr, + "output_files": [] + } + +@mcp.tool() +def ls( + path: Optional[Path] = None, + all_files: bool = False, + almost_all: bool = False, + long_format: bool = False, + recursive: bool = False, + human_readable: bool = False, + reverse: bool = False, + sort_by_time: bool = False +) -> Dict[str, Any]: + """ + List information about files and directories. + + This tool is a wrapper for the GNU 'ls' command. + """ + command = ["ls"] + if all_files: + command.append("-a") + if almost_all: + command.append("-A") + if long_format: + command.append("-l") + if recursive: + command.append("-R") + if human_readable: + command.append("-h") + if reverse: + command.append("-r") + if sort_by_time: + command.append("-t") + + if path: + if not path.exists(): + raise ValueError(f"Path does not exist: {path}") + command.append(str(path)) + + return _run_command(command) + +@mcp.tool() +def cp( + source: List[Path], + destination: Path, + recursive: bool = False, + force: bool = False, + update: bool = False, + verbose: bool = False +) -> Dict[str, Any]: + """ + Copy files and directories. + + This tool is a wrapper for the GNU 'cp' command. + """ + if not source: + raise ValueError("At least one source path must be provided.") + + command = ["cp"] + if recursive: + command.append("-R") + if force: + command.append("-f") + if update: + command.append("-u") + if verbose: + command.append("-v") + + for src_path in source: + if not src_path.exists(): + raise FileNotFoundError(f"Source path does not exist: {src_path}") + command.append(str(src_path)) + + command.append(str(destination)) + + result = _run_command(command) + + # Determine output files + output_files = [] + if destination.is_dir(): + for src in source: + output_files.append(str(destination / src.name)) + else: + output_files.append(str(destination)) + result["output_files"] = output_files + + return result + +@mcp.tool() +def mv( + source: List[Path], + destination: Path, + force: bool = False, + update: bool = False, + verbose: bool = False +) -> Dict[str, Any]: + """ + Move (rename) files and directories. + + This tool is a wrapper for the GNU 'mv' command. + """ + if not source: + raise ValueError("At least one source path must be provided.") + + command = ["mv"] + if force: + command.append("-f") + if update: + command.append("-u") + if verbose: + command.append("-v") + + for src_path in source: + if not src_path.exists(): + raise FileNotFoundError(f"Source path does not exist: {src_path}") + command.append(str(src_path)) + + command.append(str(destination)) + + result = _run_command(command) + + # Determine output files + output_files = [] + if destination.is_dir(): + for src in source: + output_files.append(str(destination / src.name)) + else: + output_files.append(str(destination)) + result["output_files"] = output_files + + return result + +@mcp.tool() +def rm( + files: List[Path], + force: bool = False, + recursive: bool = False, + verbose: bool = False +) -> Dict[str, Any]: + """ + Remove files or directories. + + This tool is a wrapper for the GNU 'rm' command. Use with caution. + """ + if not files: + raise ValueError("At least one file or directory to remove must be provided.") + + command = ["rm"] + if force: + command.append("-f") + if recursive: + command.append("-r") + if verbose: + command.append("-v") + + for file_path in files: + # We don't check for existence here, as 'rm -f' handles it. + command.append(str(file_path)) + + return _run_command(command) + +@mcp.tool() +def mkdir( + directories: List[Path], + parents: bool = False, + verbose: bool = False, + mode: Optional[str] = None +) -> Dict[str, Any]: + """ + Create directories. + + This tool is a wrapper for the GNU 'mkdir' command. + """ + if not directories: + raise ValueError("At least one directory to create must be provided.") + + command = ["mkdir"] + if parents: + command.append("-p") + if verbose: + command.append("-v") + if mode: + command.extend(["-m", mode]) + + for dir_path in directories: + command.append(str(dir_path)) + + result = _run_command(command) + result["output_files"] = [str(d) for d in directories] + return result + +@mcp.tool() +def cat( + files: List[Path], + output_file: Optional[Path] = None, + number_lines: bool = False, + number_nonblank: bool = False, + squeeze_blank: bool = False +) -> Dict[str, Any]: + """ + Concatenate files and print on the standard output. + + This tool is a wrapper for the GNU 'cat' command. + """ + if not files: + raise ValueError("At least one input file must be provided.") + + command = ["cat"] + if number_lines: + command.append("-n") + if number_nonblank: + command.append("-b") + if squeeze_blank: + command.append("-s") + + for file_path in files: + if not file_path.is_file(): + raise FileNotFoundError(f"Input file not found: {file_path}") + command.append(str(file_path)) + + return _run_command(command, output_file=output_file) + +@mcp.tool() +def wc( + files: List[Path], + lines: bool = False, + words: bool = False, + bytes: bool = False, + chars: bool = False, + max_line_length: bool = False +) -> Dict[str, Any]: + """ + Print newline, word, and byte counts for each file. + + This tool is a wrapper for the GNU 'wc' command. + If no flags are set, default 'wc' behavior (lines, words, bytes) is used. + """ + command = ["wc"] + if lines: + command.append("-l") + if words: + command.append("-w") + if bytes: + command.append("-c") + if chars: + command.append("-m") + if max_line_length: + command.append("-L") + + if not files: + raise ValueError("At least one input file must be provided.") + + for file_path in files: + if not file_path.is_file(): + raise FileNotFoundError(f"Input file not found: {file_path}") + command.append(str(file_path)) + + return _run_command(command) + +@mcp.tool() +def sort( + input_files: List[Path], + output_file: Optional[Path] = None, + unique: bool = False, + numeric_sort: bool = False, + reverse: bool = False, + key: Optional[str] = None, + field_separator: Optional[str] = None +) -> Dict[str, Any]: + """ + Sort lines of text files. + + This tool is a wrapper for the GNU 'sort' command. + """ + if not input_files: + raise ValueError("At least one input file must be provided.") + + command = ["sort"] + if unique: + command.append("-u") + if numeric_sort: + command.append("-n") + if reverse: + command.append("-r") + if key: + command.extend(["-k", key]) + if field_separator: + command.extend(["-t", field_separator]) + if output_file: + command.extend(["-o", str(output_file)]) + + for file_path in input_files: + if not file_path.is_file(): + raise FileNotFoundError(f"Input file not found: {file_path}") + command.append(str(file_path)) + + result = _run_command(command) + if output_file: + result["output_files"] = [str(output_file)] + return result + +@mcp.tool() +def uniq( + input_file: Path, + output_file: Optional[Path] = None, + count: bool = False, + repeated_only: bool = False, + unique_only: bool = False, + ignore_case: bool = False +) -> Dict[str, Any]: + """ + Report or omit repeated lines from a sorted file. + + This tool is a wrapper for the GNU 'uniq' command. Input file should be sorted. + """ + if not input_file.is_file(): + raise FileNotFoundError(f"Input file not found: {input_file}") + + command = ["uniq"] + if count: + command.append("-c") + if repeated_only: + command.append("-d") + if unique_only: + command.append("-u") + if ignore_case: + command.append("-i") + + command.append(str(input_file)) + if output_file: + command.append(str(output_file)) + + result = _run_command(command) + if output_file: + result["output_files"] = [str(output_file)] + return result + +@mcp.tool() +def head( + files: List[Path], + lines: int = 10, + bytes: Optional[int] = None, + output_file: Optional[Path] = None +) -> Dict[str, Any]: + """ + Output the first part of files. + + This tool is a wrapper for the GNU 'head' command. + """ + if not files: + raise ValueError("At least one input file must be provided.") + if bytes is not None and lines != 10: + raise ValueError("Cannot specify both 'lines' and 'bytes'.") + + command = ["head"] + if bytes is not None: + command.extend(["-c", str(bytes)]) + else: + command.extend(["-n", str(lines)]) + + for file_path in files: + if not file_path.is_file(): + raise FileNotFoundError(f"Input file not found: {file_path}") + command.append(str(file_path)) + + return _run_command(command, output_file=output_file) + +@mcp.tool() +def tail( + files: List[Path], + lines: int = 10, + bytes: Optional[int] = None, + follow: bool = False, + output_file: Optional[Path] = None +) -> Dict[str, Any]: + """ + Output the last part of files. + + This tool is a wrapper for the GNU 'tail' command. + Note: 'follow' mode may not be suitable for non-interactive server environments. + """ + if not files: + raise ValueError("At least one input file must be provided.") + if bytes is not None and lines != 10: + raise ValueError("Cannot specify both 'lines' and 'bytes'.") + + command = ["tail"] + if follow: + command.append("-f") + if bytes is not None: + command.extend(["-c", str(bytes)]) + else: + command.extend(["-n", str(lines)]) + + for file_path in files: + if not file_path.is_file(): + raise FileNotFoundError(f"Input file not found: {file_path}") + command.append(str(file_path)) + + return _run_command(command, output_file=output_file) + +@mcp.tool() +def chmod( + mode: str, + files: List[Path], + recursive: bool = False, + verbose: bool = False +) -> Dict[str, Any]: + """ + Change file mode bits. + + This tool is a wrapper for the GNU 'chmod' command. + """ + if not files: + raise ValueError("At least one file or directory must be provided.") + if not mode: + raise ValueError("A mode must be specified (e.g., '755', 'u+x').") + + command = ["chmod"] + if recursive: + command.append("-R") + if verbose: + command.append("-v") + + command.append(mode) + + for file_path in files: + if not file_path.exists(): + raise FileNotFoundError(f"File or directory not found: {file_path}") + command.append(str(file_path)) + + return _run_command(command) \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_coreutils/app/coreutils_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_coreutils/app/coreutils_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..6e838f10c9ea43524e1df8ebf91b9128a2e95dcc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_coreutils/app/coreutils_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_coreutils/app/coreutils_server.py') +SERVER_NAME = 'biosci_coreutils' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_coreutils/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_coreutils/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_coreutils/app/requirements.txt @@ -0,0 +1 @@ + diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_coreutils/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_coreutils/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..d98696b6b7e8e6767a3d933fd7bc7af5cd9a9e03 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_coreutils/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-coreutils: + build: . + image: mcp-coreutils:latest + container_name: mcp-coreutils + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=coreutils + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_coreutils/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_coreutils/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..0e6f8b3148581b4e586035ca9f001f2571f25c55 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_coreutils/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - coreutils + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_coreutils/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_coreutils/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_coreutils/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_cospar/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_cospar/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..bd0d606cfe3dd89e61ce40d207cefee33692f41d --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_cospar/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install cospar via conda (e.g., from bioconda) +RUN conda install -c bioconda cospar -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY app/cospar_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/cospar_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/cospar_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_cospar/app/cospar_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_cospar/app/cospar_server.py new file mode 100644 index 0000000000000000000000000000000000000000..aa8a4cdcca09e25e0aae84fdaada3379ac5ed96a --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_cospar/app/cospar_server.py @@ -0,0 +1,457 @@ +import io +import subprocess +import sys +from pathlib import Path +from typing import List, Literal, Optional + +# Mock mcp decorator for standalone validation. +# In a real MCP environment, this would be provided by the MCP framework. +class mcp: + @staticmethod + def tool(func): + return func + +@mcp.tool() +def iterative_clone_size_filtering( + input_adata_path: Path, + output_adata_path: Path, + min_clone_size: int = 2, + min_shared_descendants: int = 2, + n_iteration: int = 2, +) -> dict: + """ + Performs iterative clone size filtering on lineage data. + + This method is used to select informative clones for transition map inference. + It iteratively removes clones that are too small at time t_1 or t_2, and + clones that have too few shared descendants between pairs of time points. + + Args: + input_adata_path: Path to the input AnnData file (.h5ad). + output_adata_path: Path to save the output AnnData file (.h5ad). + min_clone_size: Minimum clone size to keep. + min_shared_descendants: Minimum number of shared descendants between time points. + n_iteration: Number of iterations for the filtering process. + + Returns: + A dictionary containing the command executed, stdout, stderr, and output file paths. + """ + if not input_adata_path.is_file(): + raise FileNotFoundError(f"Input AnnData file not found: {input_adata_path}") + if min_clone_size < 1: + raise ValueError("min_clone_size must be at least 1.") + if min_shared_descendants < 1: + raise ValueError("min_shared_descendants must be at least 1.") + if n_iteration < 1: + raise ValueError("n_iteration must be at least 1.") + + # Create a representative command string + command_executed = ( + f"cospar.tl.iterative_clone_size_filtering(adata, " + f"min_clone_size={min_clone_size}, " + f"min_shared_descendants={min_shared_descendants}, " + f"N_iteration={n_iteration})" + ) + + stdout_capture = io.StringIO() + stderr_capture = io.StringIO() + original_stdout, original_stderr = sys.stdout, sys.stderr + sys.stdout, sys.stderr = stdout_capture, stderr_capture + + try: + import anndata as ad + import cospar as cs + + adata = ad.read_h5ad(input_adata_path) + cs.tl.iterative_clone_size_filtering( + adata, + min_clone_size=min_clone_size, + min_shared_descendants=min_shared_descendants, + N_iteration=n_iteration, + ) + adata.write_h5ad(output_adata_path, compression="gzip") + + except Exception as e: + sys.stdout, sys.stderr = original_stdout, original_stderr + print(f"Error during CoSpar execution: {e}", file=sys.stderr) + raise + finally: + sys.stdout, sys.stderr = original_stdout, original_stderr + + return { + "command_executed": command_executed, + "stdout": stdout_capture.getvalue(), + "stderr": stderr_capture.getvalue(), + "output_files": [str(output_adata_path)], + } + + +@mcp.tool() +def infer_t_map( + input_adata_path: Path, + output_adata_path: Path, + source: Literal["state_info", "lineage", "both"] = "state_info", + epsilon: float = 0.05, + num_neighbors_s: int = 10, + num_neighbors_n: int = 30, + sparsity_threshold: float = 0.1, + map_backward: bool = True, + cospar_knn: int = 10, + w_c: float = 1.0, + w_s: float = 1.0, + noise_threshold: float = 0.2, + random_seed: int = 1, + n_jobs: int = 1, + min_cell_per_clone: int = 1, + highvar_gene_pctl: int = 85, + min_clone_num_for_fitting: int = 1, + max_clone_size_for_fitting: Optional[int] = None, + min_clone_size_for_fitting: int = 1, +) -> dict: + """ + Infers the transition map, the core function of CoSpar. + + This function integrates state and/or lineage information to infer a transition + map between cell states over time. + + Args: + input_adata_path: Path to the input AnnData file (.h5ad). + output_adata_path: Path to save the output AnnData file (.h5ad). + source: The source of information to use for inference. + epsilon: The regularization parameter for the transition map. + num_neighbors_s: Number of neighbors for state similarity graph. + num_neighbors_n: Number of neighbors for constructing the joint graph. + sparsity_threshold: Threshold to sparsify the transition map. + map_backward: If True, infer the backward transition map (t2->t1). + cospar_knn: Number of neighbors for CoSpar's KNN smoothing. + w_c: Weight for the clonal contribution. + w_s: Weight for the state similarity contribution. + noise_threshold: Threshold for identifying noisy transitions. + random_seed: Random seed for reproducibility. + n_jobs: Number of parallel jobs to use. + min_cell_per_clone: Minimum number of cells per clone to consider. + highvar_gene_pctl: Percentile for selecting highly variable genes. + min_clone_num_for_fitting: Minimum number of clones for fitting. + max_clone_size_for_fitting: Maximum clone size for fitting. If None, no limit. + min_clone_size_for_fitting: Minimum clone size for fitting. + + Returns: + A dictionary containing the command executed, stdout, stderr, and output file paths. + """ + if not input_adata_path.is_file(): + raise FileNotFoundError(f"Input AnnData file not found: {input_adata_path}") + if not (0 <= epsilon <= 1): + raise ValueError("epsilon must be between 0 and 1.") + if n_jobs < 1: + raise ValueError("n_jobs must be at least 1.") + + command_params = [ + f"source='{source}'", f"epsilon={epsilon}", f"num_neighbors_S={num_neighbors_s}", + f"num_neighbors_N={num_neighbors_n}", f"sparsity_threshold={sparsity_threshold}", + f"map_backward={map_backward}", f"CoSpar_KNN={cospar_knn}", f"w_C={w_c}", f"w_S={w_s}", + f"noise_threshold={noise_threshold}", f"random_seed={random_seed}", f"n_jobs={n_jobs}", + f"min_cell_per_clone={min_cell_per_clone}", f"HighVar_gene_pctl={highvar_gene_pctl}", + f"min_clone_num_for_fitting={min_clone_num_for_fitting}", + f"min_clone_size_for_fitting={min_clone_size_for_fitting}", + ] + if max_clone_size_for_fitting is not None: + command_params.append(f"max_clone_size_for_fitting={max_clone_size_for_fitting}") + + command_executed = f"cospar.tl.infer_T_map(adata, {', '.join(command_params)})" + + stdout_capture = io.StringIO() + stderr_capture = io.StringIO() + original_stdout, original_stderr = sys.stdout, sys.stderr + sys.stdout, sys.stderr = stdout_capture, stderr_capture + + try: + import anndata as ad + import cospar as cs + import numpy as np + + adata = ad.read_h5ad(input_adata_path) + + max_clone_size = np.inf if max_clone_size_for_fitting is None else max_clone_size_for_fitting + + cs.tl.infer_T_map( + adata, + source=source, + epsilon=epsilon, + num_neighbors_S=num_neighbors_s, + num_neighbors_N=num_neighbors_n, + sparsity_threshold=sparsity_threshold, + map_backward=map_backward, + CoSpar_KNN=cospar_knn, + w_C=w_c, + w_S=w_s, + noise_threshold=noise_threshold, + random_seed=random_seed, + n_jobs=n_jobs, + min_cell_per_clone=min_cell_per_clone, + HighVar_gene_pctl=highvar_gene_pctl, + min_clone_num_for_fitting=min_clone_num_for_fitting, + max_clone_size_for_fitting=max_clone_size, + min_clone_size_for_fitting=min_clone_size_for_fitting, + ) + adata.write_h5ad(output_adata_path, compression="gzip") + + except Exception as e: + sys.stdout, sys.stderr = original_stdout, original_stderr + print(f"Error during CoSpar execution: {e}", file=sys.stderr) + raise + finally: + sys.stdout, sys.stderr = original_stdout, original_stderr + + return { + "command_executed": command_executed, + "stdout": stdout_capture.getvalue(), + "stderr": stderr_capture.getvalue(), + "output_files": [str(output_adata_path)], + } + + +@mcp.tool() +def fate_map( + input_adata_path: Path, + output_adata_path: Path, + source: str = "transition_map", + selected_fates_str: Optional[str] = None, + map_backward: bool = True, + method: Literal["norm-sum", "sum"] = "norm-sum", + fate_count_threshold: int = 1, +) -> dict: + """ + Computes the fate map from a transition map. + + This function calculates the probability of an initial cell transitioning to + various terminal states (fates), based on the inferred transition map. + + Args: + input_adata_path: Path to the input AnnData file (.h5ad) containing a transition map. + output_adata_path: Path to save the output AnnData file (.h5ad). + source: The key in adata.uns where the transition map is stored. + selected_fates_str: A comma-separated string of cluster names to be considered as fates. + If None, uses all clusters at the later time point. + map_backward: If True, uses the backward transition map. + method: Method to compute the fate map ('norm-sum' or 'sum'). + fate_count_threshold: Minimum number of cells for a fate to be considered. + + Returns: + A dictionary containing the command executed, stdout, stderr, and output file paths. + """ + if not input_adata_path.is_file(): + raise FileNotFoundError(f"Input AnnData file not found: {input_adata_path}") + + selected_fates = selected_fates_str.split(',') if selected_fates_str else None + + command_params = [ + f"source='{source}'", f"map_backward={map_backward}", f"method='{method}'", + f"fate_count_threshold={fate_count_threshold}" + ] + if selected_fates: + command_params.append(f"selected_fates={selected_fates}") + command_executed = f"cospar.tl.fate_map(adata, {', '.join(command_params)})" + + stdout_capture = io.StringIO() + stderr_capture = io.StringIO() + original_stdout, original_stderr = sys.stdout, sys.stderr + sys.stdout, sys.stderr = stdout_capture, stderr_capture + + try: + import anndata as ad + import cospar as cs + + adata = ad.read_h5ad(input_adata_path) + cs.tl.fate_map( + adata, + source=source, + selected_fates=selected_fates, + map_backward=map_backward, + method=method, + fate_count_threshold=fate_count_threshold, + ) + adata.write_h5ad(output_adata_path, compression="gzip") + + except Exception as e: + sys.stdout, sys.stderr = original_stdout, original_stderr + print(f"Error during CoSpar execution: {e}", file=sys.stderr) + raise + finally: + sys.stdout, sys.stderr = original_stdout, original_stderr + + return { + "command_executed": command_executed, + "stdout": stdout_capture.getvalue(), + "stderr": stderr_capture.getvalue(), + "output_files": [str(output_adata_path)], + } + + +@mcp.tool() +def fate_potency( + input_adata_path: Path, + output_adata_path: Path, + map_backward: bool = True, +) -> dict: + """ + Computes fate potency from a fate map. + + Fate potency measures the diversity of fates accessible from an initial state. + It is calculated as the entropy of the fate probability distribution. + + Args: + input_adata_path: Path to the input AnnData file (.h5ad) with a computed fate map. + output_adata_path: Path to save the output AnnData file (.h5ad). + map_backward: If True, computes potency for the backward map. + + Returns: + A dictionary containing the command executed, stdout, stderr, and output file paths. + """ + if not input_adata_path.is_file(): + raise FileNotFoundError(f"Input AnnData file not found: {input_adata_path}") + + command_executed = f"cospar.tl.get_fate_potency(adata, map_backward={map_backward})" + + stdout_capture = io.StringIO() + stderr_capture = io.StringIO() + original_stdout, original_stderr = sys.stdout, sys.stderr + sys.stdout, sys.stderr = stdout_capture, stderr_capture + + try: + import anndata as ad + import cospar as cs + + adata = ad.read_h5ad(input_adata_path) + cs.tl.get_fate_potency(adata, map_backward=map_backward) + adata.write_h5ad(output_adata_path, compression="gzip") + + except Exception as e: + sys.stdout, sys.stderr = original_stdout, original_stderr + print(f"Error during CoSpar execution: {e}", file=sys.stderr) + raise + finally: + sys.stdout, sys.stderr = original_stdout, original_stderr + + return { + "command_executed": command_executed, + "stdout": stdout_capture.getvalue(), + "stderr": stderr_capture.getvalue(), + "output_files": [str(output_adata_path)], + } + + +@mcp.tool() +def fate_bias( + input_adata_path: Path, + output_adata_path: Path, + map_backward: bool = True, +) -> dict: + """ + Computes fate bias from a fate map. + + Fate bias quantifies how strongly a cell is biased towards a specific fate, + relative to its neighbors in the state manifold. + + Args: + input_adata_path: Path to the input AnnData file (.h5ad) with a computed fate map. + output_adata_path: Path to save the output AnnData file (.h5ad). + map_backward: If True, computes bias for the backward map. + + Returns: + A dictionary containing the command executed, stdout, stderr, and output file paths. + """ + if not input_adata_path.is_file(): + raise FileNotFoundError(f"Input AnnData file not found: {input_adata_path}") + + command_executed = f"cospar.tl.get_fate_bias(adata, map_backward={map_backward})" + + stdout_capture = io.StringIO() + stderr_capture = io.StringIO() + original_stdout, original_stderr = sys.stdout, sys.stderr + sys.stdout, sys.stderr = stdout_capture, stderr_capture + + try: + import anndata as ad + import cospar as cs + + adata = ad.read_h5ad(input_adata_path) + # Assumes fate_map is already in adata.uns['fate_map'] + cs.tl.get_fate_bias(adata, map_backward=map_backward) + adata.write_h5ad(output_adata_path, compression="gzip") + + except Exception as e: + sys.stdout, sys.stderr = original_stdout, original_stderr + print(f"Error during CoSpar execution: {e}", file=sys.stderr) + raise + finally: + sys.stdout, sys.stderr = original_stdout, original_stderr + + return { + "command_executed": command_executed, + "stdout": stdout_capture.getvalue(), + "stderr": stderr_capture.getvalue(), + "output_files": [str(output_adata_path)], + } + + +@mcp.tool() +def hotspot_genes( + input_adata_path: Path, + output_adata_path: Path, + fdr_cutoff: float = 0.05, + map_backward: bool = True, +) -> dict: + """ + Identifies genes correlated with fate bias (hotspot genes). + + This function performs a differential expression analysis to find genes whose + expression patterns are significantly correlated with the fate bias towards + different terminal states. + + Args: + input_adata_path: Path to the input AnnData file (.h5ad) with computed fate bias. + output_adata_path: Path to save the output AnnData file (.h5ad). + fdr_cutoff: False Discovery Rate cutoff for significance. + map_backward: If True, uses the backward map's fate bias. + + Returns: + A dictionary containing the command executed, stdout, stderr, and output file paths. + """ + if not input_adata_path.is_file(): + raise FileNotFoundError(f"Input AnnData file not found: {input_adata_path}") + if not (0 < fdr_cutoff < 1): + raise ValueError("fdr_cutoff must be between 0 and 1.") + + command_executed = ( + f"cospar.tl.get_hotspot_genes(adata, " + f"fdr_cutoff={fdr_cutoff}, map_backward={map_backward})" + ) + + stdout_capture = io.StringIO() + stderr_capture = io.StringIO() + original_stdout, original_stderr = sys.stdout, sys.stderr + sys.stdout, sys.stderr = stdout_capture, stderr_capture + + try: + import anndata as ad + import cospar as cs + + adata = ad.read_h5ad(input_adata_path) + cs.tl.get_hotspot_genes( + adata, fdr_cutoff=fdr_cutoff, map_backward=map_backward + ) + adata.write_h5ad(output_adata_path, compression="gzip") + + except Exception as e: + sys.stdout, sys.stderr = original_stdout, original_stderr + print(f"Error during CoSpar execution: {e}", file=sys.stderr) + raise + finally: + sys.stdout, sys.stderr = original_stdout, original_stderr + + return { + "command_executed": command_executed, + "stdout": stdout_capture.getvalue(), + "stderr": stderr_capture.getvalue(), + "output_files": [str(output_adata_path)], + } \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_cospar/app/cospar_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_cospar/app/cospar_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..81e577e2dda09c43674936ce5cc9f0b4f7267e19 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_cospar/app/cospar_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_cospar/app/cospar_server.py') +SERVER_NAME = 'biosci_cospar' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_cospar/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_cospar/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..2071df08468a2d45a7f42b0b738f0caf2dc0a0eb --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_cospar/app/requirements.txt @@ -0,0 +1,3 @@ +anndata==0.12.11 +cospar==0.5.0 +numpy==2.4.4 diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_cospar/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_cospar/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..b104b4c777f8e4699d5549f4d207179fa4eeb6ee --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_cospar/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-cospar: + build: . + image: mcp-cospar:latest + container_name: mcp-cospar + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=cospar + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_cospar/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_cospar/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..1f2897f3651e08be78a7ac89600a1abec2f79716 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_cospar/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - cospar + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_cospar/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_cospar/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..aec8cc71f9aa53ce09c139626c530d392672ee50 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_cospar/requirements.txt @@ -0,0 +1,5 @@ +anndata==0.12.11 +cospar==0.5.0 +numpy==2.4.4 +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_crispresso2/app/crispresso2_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_crispresso2/app/crispresso2_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..01539694fca181f56fd6f191c6153c354a4259f2 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_crispresso2/app/crispresso2_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_crispresso2/app/crispresso2_server.py') +SERVER_NAME = 'biosci_crispresso2' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_crispresso2/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_crispresso2/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_crispresso2/app/requirements.txt @@ -0,0 +1 @@ + diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_crispresso2/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_crispresso2/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..f21d1ad9ac901870e4a1b0949c818fe20e0d80b5 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_crispresso2/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-crispresso2: + build: . + image: mcp-crispresso2:latest + container_name: mcp-crispresso2 + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=crispresso2 + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_cwltool/app/cwltool_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_cwltool/app/cwltool_server.py new file mode 100644 index 0000000000000000000000000000000000000000..b24aabc5f96775ffdccb54b8dfbcc0d615ed8e51 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_cwltool/app/cwltool_server.py @@ -0,0 +1,384 @@ +import subprocess +from pathlib import Path +from typing import List, Literal, Optional + +# @mcp.tool() is a placeholder for the actual decorator in the MCP framework. +# It is used here to indicate that these functions are intended to be MCP tools. + +@mcp.tool() +def run( + cwl_document: Path, + job_order_file: Optional[Path] = None, + outdir: Optional[Path] = None, + tmpdir_prefix: Optional[str] = None, + tmp_outdir_prefix: Optional[str] = None, + leave_tmpdir: bool = False, + target: Optional[List[str]] = None, + on_error: Literal["stop", "continue"] = "stop", + compute_checksum: bool = True, + relax_path_checks: bool = False, + container_runtime: Literal["docker", "singularity", "udocker", "none"] = "docker", + user_space_docker_cmd: Optional[str] = None, + custom_net: Optional[str] = None, + force_docker_pull: bool = False, + leave_container: bool = False, + no_match_user: bool = False, + cidfile_dir: Optional[Path] = None, + cidfile_prefix: Optional[str] = None, + preserve_entire_environment: bool = False, + preserve_environment: Optional[List[str]] = None, + mpi_config_file: Optional[Path] = None, + cachedir: Optional[Path] = None, + strict: bool = False, + skip_schemas: bool = False, + doc_cache: bool = True, + json_validation: bool = True, + log_level: Literal["normal", "quiet", "debug"] = "normal", + js_console: bool = False, + fast_parser: bool = False, + eval_timeout: Optional[float] = None, + beta_dependency_resolvers_configuration: Optional[Path] = None, + overrides: Optional[Path] = None, + no_read_only: bool = False, + enable_ext: bool = False, + enable_dev: bool = False, + enable_js_req: bool = True, + enable_load_listing: Literal["deep_listing", "shallow_listing", "no_listing"] = "shallow_listing", + enable_shell_command: bool = False, + enable_network_access: bool = True, + enable_multi_tool: bool = True, + enable_sandboxjsreq: bool = False, +): + """ + Runs a CWL tool or workflow using the cwltool reference implementation. + + This is the primary function for executing CWL documents. It supports a wide + range of options for controlling execution, containerization, environment, + caching, and more. + """ + if not cwl_document.is_file(): + raise FileNotFoundError(f"CWL document not found: {cwl_document}") + if job_order_file and not job_order_file.is_file(): + raise FileNotFoundError(f"Job order file not found: {job_order_file}") + if outdir: + outdir.mkdir(parents=True, exist_ok=True) + if preserve_entire_environment and preserve_environment: + raise ValueError("Cannot use both 'preserve_entire_environment' and 'preserve_environment'.") + + cmd = ["cwltool"] + + if log_level == "quiet": + cmd.append("--quiet") + elif log_level == "debug": + cmd.append("--debug") + if js_console: + cmd.append("--js-console") + if outdir: + cmd.extend(["--outdir", str(outdir)]) + if tmpdir_prefix: + cmd.extend(["--tmpdir-prefix", tmpdir_prefix]) + if tmp_outdir_prefix: + cmd.extend(["--tmp-outdir-prefix", tmp_outdir_prefix]) + if leave_tmpdir: + cmd.append("--leave-tmpdir") + if target: + for t in target: + cmd.extend(["--target", t]) + if on_error == "continue": + cmd.extend(["--on-error", "continue"]) + if not compute_checksum: + cmd.append("--no-compute-checksum") + if relax_path_checks: + cmd.append("--relax-path-checks") + + if container_runtime == "singularity": + cmd.append("--singularity") + elif container_runtime == "udocker": + cmd.append("--udocker") + elif container_runtime == "none": + cmd.append("--no-container") + + if user_space_docker_cmd: + cmd.extend(["--user-space-docker-cmd", user_space_docker_cmd]) + if custom_net: + cmd.extend(["--custom-net", custom_net]) + if force_docker_pull: + cmd.append("--force-docker-pull") + if leave_container: + cmd.append("--leave-container") + if no_match_user: + cmd.append("--no-match-user") + if cidfile_dir: + cmd.extend(["--cidfile-dir", str(cidfile_dir)]) + if cidfile_prefix: + cmd.extend(["--cidfile-prefix", cidfile_prefix]) + + if preserve_entire_environment: + cmd.append("--preserve-entire-environment") + elif preserve_environment: + for env_var in preserve_environment: + cmd.extend(["--preserve-environment", env_var]) + if mpi_config_file: + cmd.extend(["--mpi-config-file", str(mpi_config_file)]) + + if cachedir: + cmd.extend(["--cachedir", str(cachedir)]) + if strict: + cmd.append("--strict") + if skip_schemas: + cmd.append("--skip-schemas") + if not doc_cache: + cmd.append("--no-doc-cache") + if not json_validation: + cmd.append("--no-json-validation") + + if fast_parser: + cmd.append("--fast-parser") + if eval_timeout is not None: + cmd.extend(["--eval-timeout", str(eval_timeout)]) + if beta_dependency_resolvers_configuration: + cmd.extend(["--beta-dependency-resolvers-configuration", str(beta_dependency_resolvers_configuration)]) + if overrides: + cmd.extend(["--overrides", str(overrides)]) + if no_read_only: + cmd.append("--no-read-only") + + if enable_ext: + cmd.append("--enable-ext") + if enable_dev: + cmd.append("--enable-dev") + if not enable_js_req: + cmd.append("--disable-js-req") + cmd.extend(["--enable-load-listing", enable_load_listing]) + if enable_shell_command: + cmd.append("--enable-shell-command") + if not enable_network_access: + cmd.append("--disable-network-access") + if not enable_multi_tool: + cmd.append("--disable-multi-tool") + if enable_sandboxjsreq: + cmd.append("--enable-sandboxjsreq") + + cmd.append(str(cwl_document)) + if job_order_file: + cmd.append(str(job_order_file)) + + command_str = " ".join(cmd) + try: + result = subprocess.run(cmd, capture_output=True, text=True, check=True) + output_files = [] + if outdir: + output_files = [str(p) for p in outdir.glob("**/*") if p.is_file()] + + return { + "command_executed": command_str, + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": output_files + } + except subprocess.CalledProcessError as e: + return { + "command_executed": command_str, + "stdout": e.stdout, + "stderr": e.stderr, + "error": f"cwltool failed with exit code {e.returncode}" + } + +@mcp.tool() +def pack( + cwl_document: Path, + output_file: Optional[Path] = None +): + """ + Combines a CWL workflow and its dependencies into a single compound document. + + This operation takes all the CWL files referenced by a workflow and builds a + new CWL document with all Process objects in a '$graph' list. + """ + if not cwl_document.is_file(): + raise FileNotFoundError(f"CWL document not found: {cwl_document}") + + cmd = ["cwltool", "--pack", str(cwl_document)] + command_str = " ".join(cmd) + + try: + result = subprocess.run(cmd, capture_output=True, text=True, check=True) + + output_files = [] + if output_file: + output_file.parent.mkdir(parents=True, exist_ok=True) + with open(output_file, "w") as f: + f.write(result.stdout) + output_files.append(str(output_file)) + + return { + "command_executed": command_str, + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": output_files + } + except subprocess.CalledProcessError as e: + return { + "command_executed": command_str, + "stdout": e.stdout, + "stderr": e.stderr, + "error": f"cwltool --pack failed with exit code {e.returncode}" + } + +@mcp.tool() +def print_targets( + cwl_document: Path +): + """ + Lists the available targets (steps, inputs, outputs) in a CWL workflow. + + Targets can be used with the 'run' function for partial workflow execution. + """ + if not cwl_document.is_file(): + raise FileNotFoundError(f"CWL document not found: {cwl_document}") + + cmd = ["cwltool", "--print-targets", str(cwl_document)] + command_str = " ".join(cmd) + + try: + result = subprocess.run(cmd, capture_output=True, text=True, check=True) + return { + "command_executed": command_str, + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": [] + } + except subprocess.CalledProcessError as e: + return { + "command_executed": command_str, + "stdout": e.stdout, + "stderr": e.stderr, + "error": f"cwltool --print-targets failed with exit code {e.returncode}" + } + +@mcp.tool() +def print_subgraph( + cwl_document: Path, + target: List[str], + output_file: Optional[Path] = None +): + """ + Prints the CWL subgraph for selected targets. + + This is useful for creating a new, partial CWL workflow file that starts + from or leads to specific steps or outputs. + """ + if not cwl_document.is_file(): + raise FileNotFoundError(f"CWL document not found: {cwl_document}") + if not target: + raise ValueError("'target' list cannot be empty for print_subgraph.") + + cmd = ["cwltool", "--print-subgraph"] + for t in target: + cmd.extend(["--target", t]) + cmd.append(str(cwl_document)) + + command_str = " ".join(cmd) + + try: + result = subprocess.run(cmd, capture_output=True, text=True, check=True) + + output_files = [] + if output_file: + output_file.parent.mkdir(parents=True, exist_ok=True) + with open(output_file, "w") as f: + f.write(result.stdout) + output_files.append(str(output_file)) + + return { + "command_executed": command_str, + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": output_files + } + except subprocess.CalledProcessError as e: + return { + "command_executed": command_str, + "stdout": e.stdout, + "stderr": e.stderr, + "error": f"cwltool --print-subgraph failed with exit code {e.returncode}" + } + +@mcp.tool() +def print_dot( + cwl_document: Path, + output_file: Optional[Path] = None +): + """ + Generates a workflow visualization in Graphviz 'dot' format. + + The output can be rendered into an image (e.g., SVG, PNG) using the 'dot' command-line tool. + """ + if not cwl_document.is_file(): + raise FileNotFoundError(f"CWL document not found: {cwl_document}") + + cmd = ["cwltool", "--print-dot", str(cwl_document)] + command_str = " ".join(cmd) + + try: + result = subprocess.run(cmd, capture_output=True, text=True, check=True) + + output_files = [] + if output_file: + output_file.parent.mkdir(parents=True, exist_ok=True) + with open(output_file, "w") as f: + f.write(result.stdout) + output_files.append(str(output_file)) + + return { + "command_executed": command_str, + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": output_files + } + except subprocess.CalledProcessError as e: + return { + "command_executed": command_str, + "stdout": e.stdout, + "stderr": e.stderr, + "error": f"cwltool --print-dot failed with exit code {e.returncode}" + } + +@mcp.tool() +def print_rdf( + cwl_document: Path, + rdf_serializer: Literal["turtle", "n3", "nt", "xml", "json-ld"] = "turtle", + output_file: Optional[Path] = None +): + """ + Models a CWL document as an RDF graph and prints it in the specified format. + """ + if not cwl_document.is_file(): + raise FileNotFoundError(f"CWL document not found: {cwl_document}") + + cmd = ["cwltool", "--print-rdf", "--rdf-serializer", rdf_serializer, str(cwl_document)] + command_str = " ".join(cmd) + + try: + result = subprocess.run(cmd, capture_output=True, text=True, check=True) + + output_files = [] + if output_file: + output_file.parent.mkdir(parents=True, exist_ok=True) + with open(output_file, "w") as f: + f.write(result.stdout) + output_files.append(str(output_file)) + + return { + "command_executed": command_str, + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": output_files + } + except subprocess.CalledProcessError as e: + return { + "command_executed": command_str, + "stdout": e.stdout, + "stderr": e.stderr, + "error": f"cwltool --print-rdf failed with exit code {e.returncode}" + } \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_dca/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_dca/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..711ea217d5954a4d0531722c43adaf4b207c10a9 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_dca/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install dca via conda (e.g., from bioconda) +RUN conda install -c bioconda dca -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY app/dca_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/dca_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/dca_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_dca/app/dca_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_dca/app/dca_server.py new file mode 100644 index 0000000000000000000000000000000000000000..66c6fa4274786363c4de3f44f9e637ba7e42486e --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_dca/app/dca_server.py @@ -0,0 +1,200 @@ +import logging +import subprocess +from pathlib import Path +from typing import Dict, List, Optional + +# Configure logging +logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') + +# @mcp.tool() decorator is assumed to be available in the execution environment. +# We define the function as if it were present. + +def run_dca( + input_file: Path, + output_path: Path, + ae_type: str = 'zinb', + norm_type: str = 'zheng', + hidden_size: List[int] = [64, 32, 64], + hyper_search: bool = False, + hyper_param: List[str] = ['hidden_size', 'learning_rate', 'dropout'], + hyper_evals: int = 50, + normalize: bool = True, + log_transform: bool = True, + scale: bool = True, + epochs: int = 300, + reduce_lr_on_plateau: int = 10, + early_stop: int = 15, + batch_size: int = 32, + dropout: float = 0.0, + learning_rate: float = 0.001, + gpus: Optional[str] = None, + save_weights: bool = False, + load_weights: Optional[Path] = None, + pretune: bool = True, + seed: int = 42, + verbose: bool = False, + cli_progress: bool = True, +) -> Dict: + """ + Denoise single-cell RNA-seq data using a deep count autoencoder (DCA). + + This tool takes a raw count matrix and uses a deep autoencoder with a + zero-inflated negative binomial (ZINB) loss function to remove dropout effects. + + Args: + input_file: Path to the input count matrix file (TSV/CSV format). + output_path: Path to the directory where output files will be saved. + ae_type: Type of autoencoder ('zinb' or 'nb'). + norm_type: Type of size factor estimation ('zheng', 'deseq', 'log', 'none'). + hidden_size: List of integers representing the hidden layer sizes. + hyper_search: If True, perform hyperparameter search. + hyper_param: List of hyperparameters to be optimized during search. + hyper_evals: Number of evaluations in hyperparameter search. + normalize: If False, do not normalize the data (--no-norm). + log_transform: If False, do not log-transform the data (--no-log). + scale: If False, do not scale the data (--no-scale). + epochs: Number of training epochs. + reduce_lr_on_plateau: Reduce learning rate on plateau (patience). + early_stop: Early stopping (patience). + batch_size: Training batch size. + dropout: Dropout rate. + learning_rate: Learning rate for the optimizer. + gpus: GPU devices to use (e.g., "0" or "0,1"). + save_weights: If True, save model weights. + load_weights: Path to a file to load model weights from. + pretune: If False, do not pre-tune the autoencoder (--no-pretune). + seed: Random seed for reproducibility. + verbose: If True, show verbose output. + cli_progress: If False, do not show the CLI progress bar (--no-cli-progress). + + Returns: + A dictionary containing the executed command, stdout, stderr, and a + dictionary of output file paths. + """ + # 1. Input Validation + if not input_file.is_file(): + raise FileNotFoundError(f"Input file not found: {input_file}") + + if load_weights and not load_weights.is_file(): + raise FileNotFoundError(f"Weights file for loading not found: {load_weights}") + + allowed_ae_types = ['zinb', 'nb'] + if ae_type not in allowed_ae_types: + raise ValueError(f"Invalid ae_type '{ae_type}'. Must be one of {allowed_ae_types}.") + + allowed_norm_types = ['zheng', 'deseq', 'log', 'none'] + if norm_type not in allowed_norm_types: + raise ValueError(f"Invalid norm_type '{norm_type}'. Must be one of {allowed_norm_types}.") + + if not hidden_size: + raise ValueError("hidden_size list cannot be empty.") + + if not (0.0 <= dropout < 1.0): + raise ValueError("dropout must be between 0.0 and 1.0 (exclusive of 1.0).") + + if epochs <= 0 or hyper_evals <= 0 or batch_size <= 0: + raise ValueError("epochs, hyper_evals, and batch_size must be positive integers.") + + # 2. Command Construction + cmd = ["dca"] + + # Add optional arguments if they differ from the tool's defaults + if ae_type != 'zinb': + cmd.extend(["--type", ae_type]) + if norm_type != 'zheng': + cmd.extend(["--normtype", norm_type]) + if hidden_size != [64, 32, 64]: + cmd.extend(["--hidden-size"] + [str(s) for s in hidden_size]) + if hyper_search: + cmd.append("--hyper") + if hyper_param != ['hidden_size', 'learning_rate', 'dropout']: + cmd.extend(["--hyper-param"] + hyper_param) + if hyper_evals != 50: + cmd.extend(["--hyper-evals", str(hyper_evals)]) + if not normalize: + cmd.append("--no-norm") + if not log_transform: + cmd.append("--no-log") + if not scale: + cmd.append("--no-scale") + if epochs != 300: + cmd.extend(["--epochs", str(epochs)]) + if reduce_lr_on_plateau != 10: + cmd.extend(["--reduce-lr", str(reduce_lr_on_plateau)]) + if early_stop != 15: + cmd.extend(["--early-stop", str(early_stop)]) + if batch_size != 32: + cmd.extend(["--batch-size", str(batch_size)]) + if dropout != 0.0: + cmd.extend(["--dropout", str(dropout)]) + if learning_rate != 0.001: + cmd.extend(["--learning-rate", str(learning_rate)]) + if gpus: + cmd.extend(["--gpus", gpus]) + if save_weights: + cmd.append("--save-weights") + if load_weights: + cmd.extend(["--load-weights", str(load_weights)]) + if not pretune: + cmd.append("--no-pretune") + if seed != 42: + cmd.extend(["--seed", str(seed)]) + if verbose: + cmd.append("--verbose") + if not cli_progress: + cmd.append("--no-cli-progress") + + # Add positional arguments + cmd.extend([str(input_file), str(output_path)]) + + command_executed = " ".join(cmd) + logging.info(f"Executing command: {command_executed}") + + # 3. Subprocess Execution + try: + result = subprocess.run( + cmd, + check=True, + capture_output=True, + text=True + ) + except FileNotFoundError: + logging.error("The 'dca' executable was not found in the system's PATH.") + raise + except subprocess.CalledProcessError as e: + logging.error(f"DCA tool failed with exit code {e.returncode}") + logging.error(f"Stdout: {e.stdout}") + logging.error(f"Stderr: {e.stderr}") + raise + + # 4. Output Parsing and Structuring + output_files = {} + expected_files = { + "mean": "mean.tsv", + "normalized_mean": "mean_norm.tsv", + "pi": "pi.tsv", + "dispersion": "dispersion.tsv", + "reduced_representation": "reduced.tsv", + } + + for key, filename in expected_files.items(): + file_path = output_path / filename + if file_path.is_file(): + output_files[key] = str(file_path) + else: + logging.warning(f"Expected output file not found: {file_path}") + + if save_weights: + weights_path = output_path / "weights.hdf5" + if weights_path.is_file(): + output_files["weights"] = str(weights_path) + else: + logging.warning(f"Expected weights file not found: {weights_path}") + + # 5. Return structured result + return { + "command_executed": command_executed, + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": output_files + } \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_dca/app/dca_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_dca/app/dca_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..4a0b83a9bc264e95a857a3291894036caa4ff082 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_dca/app/dca_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_dca/app/dca_server.py') +SERVER_NAME = 'biosci_dca' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_dca/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_dca/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_dca/app/requirements.txt @@ -0,0 +1 @@ + diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_dca/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_dca/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..028f9bb4b02dc966952ae4a59cddb09d44657f60 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_dca/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-dca: + build: . + image: mcp-dca:latest + container_name: mcp-dca + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=dca + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_dca/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_dca/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..ad6dfb7df7bef5fb4ff3eb9c878ea82e1af72ea0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_dca/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - dca + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_dca/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_dca/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_dca/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_deblur/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_deblur/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..aa5593e7ae68e9f1a9a23f948bcd86efa452307e --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_deblur/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install deblur via conda (e.g., from bioconda) +RUN conda install -c bioconda deblur -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY app/deblur_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/deblur_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/deblur_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_deblur/app/deblur_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_deblur/app/deblur_server.py new file mode 100644 index 0000000000000000000000000000000000000000..95624f04ff784613ae8821515ec753710459e552 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_deblur/app/deblur_server.py @@ -0,0 +1,147 @@ +import subprocess +import tempfile +from pathlib import Path +from typing import Optional, List + +# This is a placeholder for the MCP decorator. +# In a real MCP environment, this would be provided by the MCP framework. +def tool(*args, **kwargs): + def decorator(func): + return func + return decorator + +mcp = type("mcp", (), {"tool": tool}) + + +@mcp.tool() +def deblur_workflow( + seqs_fp: Path, + output_dir: Path, + trim_length: int, + min_reads: int = 10, + threads_to_use: int = 1, + pos_ref_fp: Optional[Path] = None, + neg_ref_fp: Optional[Path] = None, +) -> dict: + """ + Run the Deblur workflow for denoising amplicon sequencing data. + + Deblur is a greedy deconvolution algorithm that leverages known error profiles + to identify and remove sequencing noise, resulting in high-resolution sOTUs + (sub-Operational Taxonomic Units). This workflow command is the primary + entry point for running the full Deblur pipeline. + + Args: + seqs_fp: Path to the input sequences. This can be a single demultiplexed + FASTA/FASTQ file or a directory containing multiple per-sample + FASTA/FASTQ files. Files can be gzipped. + output_dir: Path to the directory where output files will be written. + It will be created if it does not exist. + trim_length: The length to trim all sequences to. All reads shorter + than this length will be discarded. A value of -1 + disables trimming, which is only recommended if all + reads are already of a uniform length. + min_reads: The minimum number of reads for an sOTU to be retained. + This filter is applied across all samples. Default is 10. + Set to 0 to disable this filtering step. + threads_to_use: The number of threads to use for parallel processing. + Default is 1. + pos_ref_fp: Path to the FASTA file for positive filtering. sOTUs that + align to this reference are kept in the 'reference-hit' + output. By default, a 16S reference is used. + neg_ref_fp: Path to the FASTA file for negative filtering. sOTUs that + align to this reference (e.g., PhiX, adapters) are + discarded. By default, a PhiX/adapter reference is used. + + Returns: + A dictionary containing the execution details and paths to output files. + """ + # Input validation + if not seqs_fp.exists(): + raise FileNotFoundError(f"Input sequence path does not exist: {seqs_fp}") + if trim_length < -1: + raise ValueError("trim_length must be a positive integer or -1 to disable.") + if min_reads < 0: + raise ValueError("min_reads must be a non-negative integer.") + if threads_to_use <= 0: + raise ValueError("threads_to_use must be a positive integer.") + if pos_ref_fp and not pos_ref_fp.is_file(): + raise FileNotFoundError(f"Positive reference file not found: {pos_ref_fp}") + if neg_ref_fp and not neg_ref_fp.is_file(): + raise FileNotFoundError(f"Negative reference file not found: {neg_ref_fp}") + + # Create output directory if it doesn't exist + output_dir.mkdir(parents=True, exist_ok=True) + + # Construct the command + cmd = [ + "deblur", + "workflow", + "--seqs-fp", + str(seqs_fp), + "--output-dir", + str(output_dir), + "-t", + str(trim_length), + "--min-reads", + str(min_reads), + "-O", + str(threads_to_use), + ] + + if pos_ref_fp: + cmd.extend(["--pos-ref-fp", str(pos_ref_fp)]) + if neg_ref_fp: + cmd.extend(["--neg-ref-fp", str(neg_ref_fp)]) + + command_executed = " ".join(cmd) + + # Execute the command + try: + result = subprocess.run( + cmd, + capture_output=True, + text=True, + check=True, + ) + except subprocess.CalledProcessError as e: + return { + "command_executed": command_executed, + "stdout": e.stdout, + "stderr": e.stderr, + "return_code": e.returncode, + "error": "Deblur workflow failed.", + "output_files": [], + } + except FileNotFoundError: + return { + "command_executed": command_executed, + "stdout": "", + "stderr": "deblur command not found. Make sure it is in your PATH.", + "return_code": 1, + "error": "Command not found.", + "output_files": [], + } + + # Identify and list output files + output_files = { + "all_biom": output_dir / "all.biom", + "all_seqs": output_dir / "all.seqs.fa", + "reference_hit_biom": output_dir / "reference-hit.biom", + "reference_hit_seqs": output_dir / "reference-hit.seqs.fa", + "reference_non_hit_biom": output_dir / "reference-non-hit.biom", + "reference_non_hit_seqs": output_dir / "reference-non-hit.seqs.fa", + } + + # Verify that expected output files were created + existing_files = { + key: str(path) for key, path in output_files.items() if path.exists() + } + + return { + "command_executed": command_executed, + "stdout": result.stdout, + "stderr": result.stderr, + "return_code": result.returncode, + "output_files": existing_files, + } \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_deblur/app/deblur_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_deblur/app/deblur_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..69da10b0d4078bd01c996828216538244f373b61 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_deblur/app/deblur_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_deblur/app/deblur_server.py') +SERVER_NAME = 'biosci_deblur' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_deblur/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_deblur/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_deblur/app/requirements.txt @@ -0,0 +1 @@ + diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_deblur/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_deblur/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..af47e06e09893b3d16fe076ee9d5c2354a929bd7 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_deblur/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-deblur: + build: . + image: mcp-deblur:latest + container_name: mcp-deblur + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=deblur + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_deblur/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_deblur/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..e98338b0570effa7e9116f80d7eb942881c465f5 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_deblur/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - deblur + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_deblur/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_deblur/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_deblur/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_dnaio/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_dnaio/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..4a6248af07c327b836dbbcba8614614a5ef1bd5d --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_dnaio/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install dnaio via conda (e.g., from bioconda) +RUN conda install -c bioconda dnaio -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY app/dnaio_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/dnaio_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/dnaio_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_dnaio/app/dnaio_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_dnaio/app/dnaio_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..f82d9cc00d8b165c8a52238d0a0b2ce1b08b2a6c --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_dnaio/app/dnaio_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_dnaio/app/dnaio_server.py') +SERVER_NAME = 'biosci_dnaio' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_dnaio/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_dnaio/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..dbda3b4accdf3423e87829527f249f3a8673120a --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_dnaio/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-dnaio: + build: . + image: mcp-dnaio:latest + container_name: mcp-dnaio + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=dnaio + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_dnaio/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_dnaio/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..1423be2bb7ab06e8fa244620baa4c923209d88ce --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_dnaio/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - dnaio + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_dnaio/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_dnaio/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_dnaio/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_doubletdetection/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_doubletdetection/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..6a3fcfaef99cb0ab7322c7aa6c5dc2ddf197b246 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_doubletdetection/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install doubletdetection via conda (e.g., from bioconda) +RUN conda install -c bioconda doubletdetection -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY doubletdetection_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/doubletdetection_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/doubletdetection_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_doubletdetection/app/doubletdetection_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_doubletdetection/app/doubletdetection_server.py new file mode 100644 index 0000000000000000000000000000000000000000..9454bdf323389b12b65bab68c1ddb2e3c28766b5 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_doubletdetection/app/doubletdetection_server.py @@ -0,0 +1,264 @@ +from pathlib import Path +import subprocess +import sys +import tempfile +from typing import Optional + +@mcp.tool() +def boost_classifier( + input_path: str, + output_path: str, + n_iters: int = 25, + clustering_algorithm: str = "louvain", + standard_scaling: bool = False, + p_thresh: float = 1e-7, + voter_thresh: float = 0.5, + n_jobs: int = -1, + random_state: int = 123, + n_top_genes: int = 10000, + pseudocount: float = 0.1, + save_model_path: Optional[str] = None, +): + """ + Run DoubletDetection BoostClassifier to detect doublets in scRNA-seq data. + + Args: + input_path: Path to input file (.h5ad, .csv, .tsv, or .mtx). + output_path: Path to save the results (AnnData .h5ad or .csv). + n_iters: Number of iterations for doublet simulation. + clustering_algorithm: Clustering method to use ('louvain', 'leiden', or 'phenograph'). + standard_scaling: Whether to use standard scaling. + p_thresh: P-value threshold for doublet calling. + voter_thresh: Voter threshold for doublet calling. + n_jobs: Number of jobs for parallel processing (-1 for all processors). + random_state: Random seed for reproducibility. + n_top_genes: Number of top genes to use. + pseudocount: Pseudocount for log transformation. + save_model_path: Optional path to save the fitted classifier object (as a pickle file) for plotting. + """ + # Input validation + input_file = Path(input_path) + if not input_file.exists(): + return {"error": f"Input file not found: {input_path}"} + + output_file = Path(output_path) + + if n_iters <= 0: + return {"error": "n_iters must be a positive integer"} + + valid_algorithms = ["louvain", "leiden", "phenograph"] + if clustering_algorithm not in valid_algorithms: + return {"error": f"clustering_algorithm must be one of {valid_algorithms}"} + + if not (0 <= p_thresh <= 1): + return {"error": "p_thresh must be between 0 and 1"} + + if not (0 <= voter_thresh <= 1): + return {"error": "voter_thresh must be between 0 and 1"} + + # Create a temporary python script to run the analysis + with tempfile.NamedTemporaryFile(mode='w', suffix='.py', delete=False) as tmp: + script_path = tmp.name + tmp.write(f""" +import doubletdetection +import scanpy as sc +import pandas as pd +import numpy as np +import pickle +from pathlib import Path + +def run(): + input_path = r"{input_path}" + output_path = r"{output_path}" + + # Load data + if input_path.endswith('.h5ad'): + adata = sc.read_h5ad(input_path) + raw_counts = adata.X + elif input_path.endswith('.csv'): + raw_counts = pd.read_csv(input_path, index_col=0) + elif input_path.endswith('.tsv'): + raw_counts = pd.read_csv(input_path, sep='\\t', index_col=0) + else: + # Fallback for other formats supported by scanpy + adata = sc.read(input_path) + raw_counts = adata.X + + # Initialize classifier + clf = doubletdetection.BoostClassifier( + n_iters={n_iters}, + clustering_algorithm="{clustering_algorithm}", + standard_scaling={standard_scaling}, + n_jobs={n_jobs}, + random_state={random_state}, + n_top_genes={n_top_genes}, + pseudocount={pseudocount} + ) + + # Fit and Predict + labels = clf.fit(raw_counts).predict(p_thresh={p_thresh}, voter_thresh={voter_thresh}) + scores = clf.doublet_score() + + # Save results + if input_path.endswith('.h5ad'): + adata.obs['doublet_score'] = scores + adata.obs['doublet_label'] = labels + adata.write(output_path) + else: + results = pd.DataFrame({{ + 'doublet_score': scores, + 'doublet_label': labels + }}, index=range(len(labels))) + results.to_csv(output_path) + + # Save model if requested + save_model_path = "{save_model_path if save_model_path else ''}" + if save_model_path: + with open(save_model_path, 'wb') as f: + pickle.dump(clf, f) + +if __name__ == "__main__": + run() +""") + + try: + # Execute the script + result = subprocess.run( + [sys.executable, script_path], + capture_output=True, + text=True, + check=True + ) + + return { + "command_executed": f"doubletdetection.BoostClassifier(n_iters={n_iters}, ...).fit().predict()", + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": [output_path] + ([save_model_path] if save_model_path else []) + } + except subprocess.CalledProcessError as e: + return { + "error": "DoubletDetection execution failed", + "stdout": e.stdout, + "stderr": e.stderr, + "command_executed": e.cmd + } + finally: + if Path(script_path).exists(): + Path(script_path).unlink() + +@mcp.tool() +def plot_convergence( + model_path: str, + output_plot_path: str, +): + """ + Generate a convergence plot for a fitted DoubletDetection classifier. + + Args: + model_path: Path to the pickled BoostClassifier object (saved from boost_classifier). + output_plot_path: Path to save the convergence plot (e.g., .png, .pdf). + """ + model_file = Path(model_path) + if not model_file.exists(): + return {"error": f"Model file not found: {model_path}"} + + with tempfile.NamedTemporaryFile(mode='w', suffix='.py', delete=False) as tmp: + script_path = tmp.name + tmp.write(f""" +import doubletdetection +import pickle +import matplotlib.pyplot as plt +from pathlib import Path + +with open(r"{model_path}", 'rb') as f: + clf = pickle.load(f) + +fig = doubletdetection.plot.convergence(clf, show=False, save=r"{output_plot_path}") +""") + + try: + result = subprocess.run( + [sys.executable, script_path], + capture_output=True, + text=True, + check=True + ) + return { + "command_executed": "doubletdetection.plot.convergence()", + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": [output_plot_path] + } + except subprocess.CalledProcessError as e: + return { + "error": "Plotting convergence failed", + "stdout": e.stdout, + "stderr": e.stderr + } + finally: + if Path(script_path).exists(): + Path(script_path).unlink() + +@mcp.tool() +def plot_threshold( + model_path: str, + output_plot_path: str, + p_thresh: float = 1e-7, + voter_thresh: float = 0.5, +): + """ + Generate a threshold plot for a fitted DoubletDetection classifier. + + Args: + model_path: Path to the pickled BoostClassifier object. + output_plot_path: Path to save the threshold plot (e.g., .png, .pdf). + p_thresh: P-value threshold to visualize. + voter_thresh: Voter threshold to visualize. + """ + model_file = Path(model_path) + if not model_file.exists(): + return {"error": f"Model file not found: {model_path}"} + + if not (0 <= p_thresh <= 1): + return {"error": "p_thresh must be between 0 and 1"} + + if not (0 <= voter_thresh <= 1): + return {"error": "voter_thresh must be between 0 and 1"} + + with tempfile.NamedTemporaryFile(mode='w', suffix='.py', delete=False) as tmp: + script_path = tmp.name + tmp.write(f""" +import doubletdetection +import pickle +import matplotlib.pyplot as plt +from pathlib import Path + +with open(r"{model_path}", 'rb') as f: + clf = pickle.load(f) + +fig = doubletdetection.plot.threshold(clf, p_thresh={p_thresh}, voter_thresh={voter_thresh}, show=False, save=r"{output_plot_path}") +""") + + try: + result = subprocess.run( + [sys.executable, script_path], + capture_output=True, + text=True, + check=True + ) + return { + "command_executed": f"doubletdetection.plot.threshold(p_thresh={p_thresh}, voter_thresh={voter_thresh})", + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": [output_plot_path] + } + except subprocess.CalledProcessError as e: + return { + "error": "Plotting threshold failed", + "stdout": e.stdout, + "stderr": e.stderr + } + finally: + if Path(script_path).exists(): + Path(script_path).unlink() \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_doubletdetection/app/doubletdetection_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_doubletdetection/app/doubletdetection_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..4214d1d804138525b47c784e8c6a95034dd0d674 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_doubletdetection/app/doubletdetection_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_doubletdetection/app/doubletdetection_server.py') +SERVER_NAME = 'biosci_doubletdetection' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_doubletdetection/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_doubletdetection/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..eaca19b2143554f45dac9e4dea9e53de725d956c --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_doubletdetection/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-doubletdetection: + build: . + image: mcp-doubletdetection:latest + container_name: mcp-doubletdetection + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=doubletdetection + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_doubletdetection/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_doubletdetection/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..7df192fe3bbb0d251940837524455b3b04fbe495 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_doubletdetection/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - doubletdetection + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_doubletdetection/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_doubletdetection/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_doubletdetection/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_echidna/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_echidna/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_echidna/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_epic/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_epic/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..7a80e02ba9e0507669d9918ea0e40d9ff5de69a5 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_epic/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install epic via conda (e.g., from bioconda) +RUN conda install -c bioconda epic -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY app/epic_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/epic_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/epic_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_epic/app/epic_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_epic/app/epic_server.py new file mode 100644 index 0000000000000000000000000000000000000000..6848518662d5deebceab9451c9fba189539decbf --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_epic/app/epic_server.py @@ -0,0 +1,278 @@ +import subprocess +import logging +from pathlib import Path +from typing import List, Optional + +# This is a placeholder for the MCP decorator. +# In a real MCP environment, this would be provided. +class mcp: + @staticmethod + def tool(): + def decorator(f): + return f + return decorator + +# Configure logging +logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') +logger = logging.getLogger(__name__) + +@mcp.tool() +def epic2( + treatment: List[Path], + control: List[Path], + genome: str, + outfile: Optional[Path] = None, + bigwig_file: Optional[Path] = None, + bin_size: int = 200, + gaps_allowed: int = 3, + fragment_size: int = 150, + keep_duplicates: bool = False, + paired_end: bool = False, + number_of_processes: int = 1, + chromsizes: Optional[Path] = None, + effective_genome_fraction: float = 0.75, + fdr: float = 0.05, + log2_fold_change: float = 1.0, + pseudocount: float = 1.0, + background_model: str = "negbin", + store_matrix: bool = False, + matrix_file_name: Optional[Path] = None, +) -> dict: + """ + Run epic2 for differential enrichment analysis of ChIP-seq data. + + epic2 is a tool for finding differentially enriched regions from ChIP-seq data. + It works on BAM files and identifies regions with significant signal change + between treatment and control samples. + """ + # --- Input Validation --- + if not treatment: + raise ValueError("At least one treatment file must be provided.") + if not control: + raise ValueError("At least one control file must be provided.") + + for file_path in treatment + control: + if not file_path.exists(): + raise FileNotFoundError(f"Input file not found: {file_path}") + + if bin_size <= 0: + raise ValueError("--bin-size must be a positive integer.") + if gaps_allowed < 0: + raise ValueError("--gaps-allowed must be a non-negative integer.") + if fragment_size <= 0: + raise ValueError("--fragment-size must be a positive integer.") + if number_of_processes <= 0: + raise ValueError("--number-of-processes must be a positive integer.") + if not (0 < effective_genome_fraction <= 1): + raise ValueError("--effective-genome-fraction must be between 0 and 1.") + if not (0 < fdr <= 1): + raise ValueError("--fdr must be between 0 and 1.") + if log2_fold_change < 0: + raise ValueError("--log2-fold-change must be non-negative.") + if pseudocount < 0: + raise ValueError("--pseudocount must be non-negative.") + if background_model not in ["negbin", "poisson"]: + raise ValueError(f"--background-model must be either 'negbin' or 'poisson', not '{background_model}'.") + if chromsizes and not chromsizes.exists(): + raise FileNotFoundError(f"Chromosome sizes file not found: {chromsizes}") + + # --- Command Construction --- + cmd = ["epic2"] + cmd.extend(["--treatment", *[str(p) for p in treatment]]) + cmd.extend(["--control", *[str(p) for p in control]]) + cmd.extend(["--genome", genome]) + + output_files = [] + + final_outfile = outfile if outfile else Path("epic2_output.csv") + cmd.extend(["--outfile", str(final_outfile)]) + output_files.append(str(final_outfile)) + + if bigwig_file: + cmd.extend(["--bigwig-file", str(bigwig_file)]) + output_files.append(str(bigwig_file)) + if bin_size != 200: + cmd.extend(["--bin-size", str(bin_size)]) + if gaps_allowed != 3: + cmd.extend(["--gaps-allowed", str(gaps_allowed)]) + if fragment_size != 150: + cmd.extend(["--fragment-size", str(fragment_size)]) + if keep_duplicates: + cmd.append("--keep-duplicates") + if paired_end: + cmd.append("--paired-end") + if number_of_processes != 1: + cmd.extend(["-p", str(number_of_processes)]) + if chromsizes: + cmd.extend(["--chromsizes", str(chromsizes)]) + if effective_genome_fraction != 0.75: + cmd.extend(["--effective-genome-fraction", str(effective_genome_fraction)]) + if fdr != 0.05: + cmd.extend(["--fdr", str(fdr)]) + if log2_fold_change != 1.0: + cmd.extend(["--log2-fold-change", str(log2_fold_change)]) + if pseudocount != 1.0: + cmd.extend(["--pseudocount", str(pseudocount)]) + if background_model != "negbin": + cmd.extend(["--background-model", background_model]) + + if store_matrix: + cmd.append("--store-matrix") + final_matrix_file = matrix_file_name if matrix_file_name else Path("read-counts.hdf5") + cmd.extend(["--matrix-file-name", str(final_matrix_file)]) + output_files.append(str(final_matrix_file)) + elif matrix_file_name: + logger.warning("--matrix-file-name is ignored because --store-matrix is not set.") + + # --- Subprocess Execution --- + command_executed = " ".join(cmd) + logger.info(f"Executing command: {command_executed}") + + try: + result = subprocess.run(cmd, capture_output=True, text=True, check=True) + except FileNotFoundError: + return { + "command_executed": command_executed, + "stdout": "", + "stderr": "Error: 'epic2' command not found. Please ensure it is installed and in your PATH.", + "output_files": [], + "return_code": 1 + } + except subprocess.CalledProcessError as e: + logger.error(f"epic2 execution failed with return code {e.returncode}") + return { + "command_executed": command_executed, + "stdout": e.stdout, + "stderr": e.stderr, + "output_files": [], + "return_code": e.returncode + } + + # --- Structured Result Return --- + return { + "command_executed": command_executed, + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": output_files, + "return_code": 0 + } + +@mcp.tool() +def wig_to_bigwig( + wiggle_file: Path, + chrom_sizes_file: Path, + bigwig_file: Path, +) -> dict: + """ + Convert a wiggle file to a bigwig file using epic2's utility. + + This requires the wiggle file, a chromosome sizes file, and the desired + output bigwig file path. + """ + # --- Input Validation --- + if not wiggle_file.exists(): + raise FileNotFoundError(f"Input wiggle file not found: {wiggle_file}") + if not chrom_sizes_file.exists(): + raise FileNotFoundError(f"Chromosome sizes file not found: {chrom_sizes_file}") + + bigwig_file.parent.mkdir(parents=True, exist_ok=True) + + # --- Command Construction --- + cmd = [ + "epic2", + "wig-to-bigwig", + str(wiggle_file), + str(chrom_sizes_file), + str(bigwig_file), + ] + + # --- Subprocess Execution --- + command_executed = " ".join(cmd) + logger.info(f"Executing command: {command_executed}") + + try: + result = subprocess.run(cmd, capture_output=True, text=True, check=True) + except FileNotFoundError: + return { + "command_executed": command_executed, + "stdout": "", + "stderr": "Error: 'epic2' command not found. Please ensure it is installed and in your PATH.", + "output_files": [], + "return_code": 1 + } + except subprocess.CalledProcessError as e: + logger.error(f"wig-to-bigwig execution failed with return code {e.returncode}") + return { + "command_executed": command_executed, + "stdout": e.stdout, + "stderr": e.stderr, + "output_files": [], + "return_code": e.returncode + } + + # --- Structured Result Return --- + return { + "command_executed": command_executed, + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": [str(bigwig_file)], + "return_code": 0 + } + +@mcp.tool() +def fetch_chrom_sizes( + genome: str, + outfile: Optional[Path] = None, +) -> dict: + """ + Fetch chromosome sizes for a given genome assembly from UCSC. + + The sizes can be saved to a file or printed to standard output. + """ + # --- Input Validation --- + if not genome.strip(): + raise ValueError("A genome assembly name must be provided (e.g., 'hg19').") + + if outfile: + outfile.parent.mkdir(parents=True, exist_ok=True) + + # --- Command Construction --- + cmd = ["epic2", "fetch-chrom-sizes", genome] + output_files = [] + + if outfile: + cmd.extend(["--outfile", str(outfile)]) + output_files.append(str(outfile)) + + # --- Subprocess Execution --- + command_executed = " ".join(cmd) + logger.info(f"Executing command: {command_executed}") + + try: + result = subprocess.run(cmd, capture_output=True, text=True, check=True) + except FileNotFoundError: + return { + "command_executed": command_executed, + "stdout": "", + "stderr": "Error: 'epic2' command not found. Please ensure it is installed and in your PATH.", + "output_files": [], + "return_code": 1 + } + except subprocess.CalledProcessError as e: + logger.error(f"fetch-chrom-sizes execution failed with return code {e.returncode}") + return { + "command_executed": command_executed, + "stdout": e.stdout, + "stderr": e.stderr, + "output_files": [], + "return_code": e.returncode + } + + # --- Structured Result Return --- + return { + "command_executed": command_executed, + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": output_files, + "return_code": 0 + } \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_epic/app/epic_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_epic/app/epic_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..d4efe924d4c9a06cf886ea893a7d05b9ef35999d --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_epic/app/epic_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_epic/app/epic_server.py') +SERVER_NAME = 'biosci_epic' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_epic/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_epic/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_epic/app/requirements.txt @@ -0,0 +1 @@ + diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_epic/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_epic/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..33fb0876d4cea6c747715413931fa2939b463b78 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_epic/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-epic: + build: . + image: mcp-epic:latest + container_name: mcp-epic + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=epic + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_epic/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_epic/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..8b1e6c5920ffb37a54ef8490307f98f3a9b3e7c7 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_epic/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - epic + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_epic/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_epic/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_epic/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_fastqc/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_fastqc/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..4a46b609ae07f90c3549799577d88835ea8fa245 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_fastqc/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install fastqc via conda (e.g., from bioconda) +RUN conda install -c bioconda fastqc -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY app/fastqc_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/fastqc_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/fastqc_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_fastqc/app/fastqc_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_fastqc/app/fastqc_server.py new file mode 100644 index 0000000000000000000000000000000000000000..6eba32ff1d25f07d8a3a68e4e094c6e1321f1e4f --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_fastqc/app/fastqc_server.py @@ -0,0 +1,196 @@ +import subprocess +import tempfile +from pathlib import Path +from typing import List, Optional, Literal, Dict, Any + +# @mcp.tool() is a placeholder for the actual decorator +# The code is written to be compatible with the MCP framework. + +def _get_fastqc_basename(filepath: Path) -> str: + """ + Derives the base name for FastQC output from an input filename. + FastQC removes known extensions to create its output file names. + Example: 'my_reads.fastq.gz' -> 'my_reads' + """ + name = filepath.name + # Order is important: remove compound extensions first + suffixes_to_remove = [ + ".fastq.gz", ".fq.gz", + ".fastq.bz2", ".fq.bz2", + ".txt.gz", ".txt.bz2", + ".sam.gz", ".sam.bz2", + ".bam.gz", ".bam.bz2", + ".fastq", ".fq", + ".txt", ".sam", ".bam" + ] + for suffix in suffixes_to_remove: + if name.endswith(suffix): + return name[:-len(suffix)] + + # Fallback for unknown extensions: remove the final suffix + return filepath.stem + +# @mcp.tool() +def run_fastqc( + seqfiles: List[Path], + outdir: Optional[Path] = None, + extract: bool = True, + threads: int = 1, + format: Optional[Literal["bam", "sam", "bam_mapped", "sam_mapped", "fastq"]] = None, + contaminants: Optional[Path] = None, + adapters: Optional[Path] = None, + limits: Optional[Path] = None, + kmer_size: int = 7, + quiet: bool = False, + casava: bool = False, + nano: bool = False, + nofilter: bool = False, + nogroup: bool = False, + temp_dir: Optional[Path] = None, +) -> Dict[str, Any]: + """ + Runs FastQC, a quality control tool for high throughput sequence data. + + FastQC reads a set of sequence files and produces a quality control report + in HTML format, along with a zip file containing the data. + + Args: + seqfiles: A list of sequence files to be processed (fastq, sam, bam). + outdir: Directory to save the output files. If not set, output is saved + in the same directory as the input file. This directory must exist. + extract: If True (default), uncompresses the output data to a directory. + If False, leaves the output as a .zip file. + threads: Number of files to process simultaneously. + format: Bypasses format detection and forces a specific format. + contaminants: Path to a non-default file with contaminants to screen against. + adapters: Path to a non-default file with adapter sequences to search for. + limits: Path to a non-default file specifying warn/error limits. + kmer_size: The length of k-mer to look for in the k-mer content module (2-10). + quiet: Suppress all progress messages on stdout. + casava: Input files are from raw Casava output. Groups files by sample. + nano: Input files are from a nanopore sequencer (fast5 format). + nofilter: If using --casava, do not remove reads flagged as poor quality. + nogroup: Disable grouping of bases for reads >50bp. Increases memory usage. + temp_dir: Directory for temporary files written during report generation. + + Returns: + A dictionary containing the executed command, stdout, stderr, and a list of + generated output files (HTML reports and ZIP archives). + """ + # 1. Input Validation + if not seqfiles: + raise ValueError("At least one sequence file must be provided.") + for f in seqfiles: + if not f.is_file(): + raise FileNotFoundError(f"Input file not found: {f}") + + if outdir and not outdir.is_dir(): + raise NotADirectoryError(f"Output directory does not exist or is not a directory: {outdir}") + + if contaminants and not contaminants.is_file(): + raise FileNotFoundError(f"Contaminants file not found: {contaminants}") + + if adapters and not adapters.is_file(): + raise FileNotFoundError(f"Adapters file not found: {adapters}") + + if limits and not limits.is_file(): + raise FileNotFoundError(f"Limits file not found: {limits}") + + if not (2 <= kmer_size <= 10): + raise ValueError(f"kmer_size must be between 2 and 10, but was {kmer_size}.") + + if threads < 1: + raise ValueError(f"threads must be a positive integer, but was {threads}.") + + if nofilter and not casava: + raise ValueError("--nofilter can only be used when --casava is also specified.") + + # 2. Command Construction + cmd = ["fastqc"] + + if outdir: + cmd.extend(["--outdir", str(outdir)]) + + if not extract: + cmd.append("--noextract") + + if threads > 1: + cmd.extend(["--threads", str(threads)]) + + if format: + cmd.extend(["--format", format]) + + if contaminants: + cmd.extend(["--contaminants", str(contaminants)]) + + if adapters: + cmd.extend(["--adapters", str(adapters)]) + + if limits: + cmd.extend(["--limits", str(limits)]) + + if kmer_size != 7: + cmd.extend(["--kmers", str(kmer_size)]) + + if quiet: + cmd.append("--quiet") + + if casava: + cmd.append("--casava") + + if nano: + cmd.append("--nano") + + if nofilter: + cmd.append("--nofilter") + + if nogroup: + cmd.append("--nogroup") + + if temp_dir: + cmd.extend(["--dir", str(temp_dir)]) + + cmd.extend([str(f) for f in seqfiles]) + command_executed = " ".join(cmd) + + # 3. Subprocess Execution + try: + result = subprocess.run( + cmd, + capture_output=True, + text=True, + check=True + ) + except FileNotFoundError: + raise RuntimeError("fastqc command not found. Please ensure it is in your PATH.") + except subprocess.CalledProcessError as e: + return { + "command_executed": command_executed, + "stdout": e.stdout, + "stderr": e.stderr, + "return_code": e.returncode, + "output_files": [] + } + + # 4. Output File Discovery + output_files = [] + for seqfile in seqfiles: + base_name = _get_fastqc_basename(seqfile) + output_location = outdir if outdir else seqfile.parent + + html_file = output_location / f"{base_name}_fastqc.html" + zip_file = output_location / f"{base_name}_fastqc.zip" + + if html_file.exists(): + output_files.append(str(html_file)) + if zip_file.exists(): + output_files.append(str(zip_file)) + + # 5. Structured Result Return + return { + "command_executed": command_executed, + "stdout": result.stdout, + "stderr": result.stderr, + "return_code": result.returncode, + "output_files": output_files + } \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_fastqc/app/fastqc_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_fastqc/app/fastqc_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..939e9c770605f93a4dcc60ba0254cf6a28b18bb4 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_fastqc/app/fastqc_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_fastqc/app/fastqc_server.py') +SERVER_NAME = 'biosci_fastqc' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_fastqc/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_fastqc/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_fastqc/app/requirements.txt @@ -0,0 +1 @@ + diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_fastqc/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_fastqc/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..b485c7107b50c9c6a9edf5ee3c6a0f48ba7f7d0f --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_fastqc/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-fastqc: + build: . + image: mcp-fastqc:latest + container_name: mcp-fastqc + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=fastqc + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_fastqc/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_fastqc/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..e27c18d74da1786962c7e7a84138540b00eb640a --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_fastqc/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - fastqc + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_fastqc/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_fastqc/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_fastqc/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_genenotebook/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_genenotebook/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_genenotebook/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_gffread/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_gffread/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..cb8645fc4708e0fb98fef13963f73292dc9b584e --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_gffread/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install gffread via conda (e.g., from bioconda) +RUN conda install -c bioconda gffread -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY app/gffread_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/gffread_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/gffread_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_gffread/app/gffread_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_gffread/app/gffread_server.py new file mode 100644 index 0000000000000000000000000000000000000000..e7fab064b02e2a07c2507b7100e21f5523499a6b --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_gffread/app/gffread_server.py @@ -0,0 +1,443 @@ +import subprocess +from pathlib import Path +from typing import Optional, List, Union + +@mcp.tool() +def gffread_tool( + input_gff: Optional[Path] = None, + genomic_seqs: Optional[Path] = None, + seq_info_fsize: Optional[Path] = None, + output_file: Optional[Path] = None, + track_name: Optional[str] = None, + region: Optional[str] = None, + region_fully_contained: bool = False, + junction_match: Optional[str] = None, + no_pseudo: bool = False, + ids_list: Optional[Path] = None, + nids_list: Optional[Path] = None, + max_intron: Optional[int] = None, + min_length: Optional[int] = None, + discard_single_exon: bool = False, + coding_only: bool = False, + non_coding_only: bool = False, + ignore_locus: bool = False, + add_description_attribute: bool = False, + keep_all_gff_attrs: bool = False, + keep_exon_attrs: bool = False, + move_exon_attrs_to_transcript: bool = False, + output_attrs: Optional[str] = None, + keep_genes: bool = False, + keep_comments: bool = False, + process_other_records: bool = False, + discard_in_frame_stop_codons: bool = False, + adjust_cds_phase: bool = False, + check_single_exon_opposite_strand: bool = False, + add_coding_status_attrs: bool = False, + add_has_cds_attribute: bool = False, + adjust_stop_codon: bool = False, + discard_non_canonical_splice_sites: bool = False, + discard_incomplete_cds: bool = False, + input_is_bed: bool = False, + input_is_tlf: bool = False, + stream_processing: bool = False, + merge_transcripts: bool = False, + dup_info_file: Optional[Path] = None, + cluster_only: bool = False, + discard_shorter_contained: bool = False, + relax_boundary_containment: bool = False, + discard_overlapping_single_exon: bool = False, + force_exons: bool = False, + gene_to_exon: bool = False, + transcript_adopt_parent: bool = False, + decode_url_chars: bool = False, + merge_close_exons: bool = False, + output_junctions: bool = False, + exons_fasta_file: Optional[Path] = None, + w_add_bases: Optional[int] = None, + w_no_cds_info: bool = False, + cds_fasta_file: Optional[Path] = None, + tr_cds_fasta_file: Optional[Path] = None, + write_exon_coords_to_defline: bool = False, + use_star_for_stop_codon: bool = False, + ensembl_gtf_to_gff3: bool = False, + chr_replace_map: Optional[Path] = None, + output_gtf: bool = False, + output_bed: bool = False, + output_tlf: bool = False, + output_table: Optional[str] = None, + expose_warnings: bool = False, + sort_alphabetically: bool = False, + sort_by_ref_list: Optional[Path] = None, +) -> dict: + """ + Filter, convert, or cluster GFF/GTF/BED records, extract transcript sequences, and more. + + By default (i.e. without -O) only transcripts are processed, discarding any + other non-transcript features. Default output is a simplified GFF3 with only + the basic attributes. + + Args: + input_gff: Path to the input GFF/GTF/BED file. If not provided, gffread reads from stdin. + genomic_seqs: Full path to a multi-fasta file with genomic sequences, OR a directory + with single-fasta files (one per genomic sequence). Required for sequence extraction + when using options like -w, -x, -y, -V, -B, -P, --adj-stop. + seq_info_fsize: Tab-delimited file providing sequence info for -A option. + Format: . + output_file: Write the output records into this file instead of stdout. + track_name: Use in the 2nd column of each GFF/GTF output line. + region: Only show transcripts overlapping coordinate range .. + (on chromosome/contig , strand if provided). Format: `[]:-`. + region_fully_contained: For `region` option, discard all transcripts that are not fully + contained within the given range. + junction_match: Only output transcripts matching the given junction. Format: `:-`. + no_pseudo: Filter out records matching the 'pseudo' keyword. + ids_list: Discard records/transcripts if their IDs are not listed in this file. + nids_list: Discard records/transcripts if their IDs are listed in this file. + Mutually exclusive with `ids_list`. + max_intron: Discard transcripts having an intron larger than this value. Must be non-negative. + min_length: Discard transcripts shorter than this value (bases). Must be non-negative. + discard_single_exon: Discard single-exon transcripts. + coding_only: Discard mRNAs that have no CDS features. Mutually exclusive with `non_coding_only`. + non_coding_only: Discard mRNAs that have CDS features. Mutually exclusive with `coding_only`. + ignore_locus: Discard locus features and attributes found in the input. + add_description_attribute: Use the description field from `seq_info_fsize` and add it + as the value for a 'descr' attribute to the GFF record. Requires `seq_info_fsize`. + keep_all_gff_attrs: Keep all GFF attributes (for non-exon features). + keep_exon_attrs: For `keep_all_gff_attrs` option, do not attempt to reduce redundant + exon/CDS attributes. + move_exon_attrs_to_transcript: Do not keep exon attributes, move them to the transcript feature + (for GFF3 output). + output_attrs: Only output the GTF/GFF attributes listed in this comma-delimited list. + keep_genes: In transcript-only mode (default), also preserve gene records. + keep_comments: For GFF3 input/output, try to preserve comments. + process_other_records: Process other non-transcript GFF records (by default non-transcript + records are ignored). + discard_in_frame_stop_codons: Discard any mRNAs with CDS having in-frame stop codons. Requires `genomic_seqs`. + adjust_cds_phase: For `discard_in_frame_stop_codons` option, check and adjust the starting CDS phase + if the original phase leads to a translation with an in-frame stop codon. + check_single_exon_opposite_strand: For `discard_in_frame_stop_codons` option, single-exon transcripts + are also checked on the opposite strand. Requires `genomic_seqs`. + add_coding_status_attrs: Add transcript level GFF attributes about the coding status of each + transcript, including partialness or in-frame stop codons. Requires `genomic_seqs`. + add_has_cds_attribute: Add a "hasCDS" attribute with value "true" for transcripts that have CDS features. + adjust_stop_codon: Stop codon adjustment: enables `add_coding_status_attrs` and performs automatic + adjustment of the CDS stop coordinate if premature or downstream. + Implicitly enables -P, which requires `genomic_seqs`. + discard_non_canonical_splice_sites: Discard multi-exon mRNAs that have any intron with a non-canonical + splice site consensus (i.e. not GT-AG, GC-AG or AT-AC). + discard_incomplete_cds: Discard any mRNAs that either lack initial START codon + or the terminal STOP codon, or have an in-frame stop codon + (i.e. only print mRNAs with a complete CDS). + input_is_bed: Input should be parsed as BED format (automatic if the input filename ends with .bed*). + input_is_tlf: Input GFF-like one-line-per-transcript format without exon/CDS features + (automatic if the input filename ends with .tlf). + stream_processing: Fast processing of input GFF/BED transcripts as they are received + (no sorting, exons must be grouped by transcript in the input data). + merge_transcripts: Cluster the input transcripts into loci, discarding "redundant" transcripts + (those with the same exact introns and fully contained or equal boundaries). + Mutually exclusive with `cluster_only`. + dup_info_file: For `merge_transcripts` option, write duplication info to this file. + cluster_only: Same as `merge_transcripts` but without discarding any of the "duplicate" transcripts, + only create "locus" features. Mutually exclusive with `merge_transcripts`. + discard_shorter_contained: For `merge_transcripts` or `cluster_only` option: also discard as redundant + the shorter, fully contained transcripts (intron chains matching a part of the container). + relax_boundary_containment: For `merge_transcripts` or `cluster_only` option, no longer require boundary + containment when assessing redundancy (can be combined with `discard_shorter_contained`). + discard_overlapping_single_exon: For `merge_transcripts` or `cluster_only` option, enforce `relax_boundary_containment` + but also discard overlapping single-exon transcripts, even on the opposite strand + (can be combined with `discard_shorter_contained`). + force_exons: Make sure that the lowest level GFF features are considered "exon" features. + gene_to_exon: For single-line genes not parenting any transcripts, add an exon feature + spanning the entire gene (treat it as a transcript). + transcript_adopt_parent: Try to find a parent gene overlapping/containing a transcript + that does not have any explicit gene Parent. + decode_url_chars: Decode url encoded characters within attributes. + merge_close_exons: Merge very close exons into a single exon (when intron size<4). + output_junctions: Output the junctions and the corresponding transcripts. + exons_fasta_file: Write a fasta file with spliced exons for each transcript. Requires `genomic_seqs`. + w_add_bases: For the `exons_fasta_file` option, extract additional bases + both upstream and downstream of the transcript boundaries. Must be non-negative. + w_no_cds_info: For `exons_fasta_file`, disable the output of CDS info in the FASTA file. + cds_fasta_file: Write a fasta file with spliced CDS for each GFF transcript. Requires `genomic_seqs`. + tr_cds_fasta_file: Write a protein fasta file with the translation of CDS for each record. Requires `genomic_seqs`. + write_exon_coords_to_defline: For `exons_fasta_file`, `cds_fasta_file`, and `tr_cds_fasta_file` options, + write in the FASTA defline all the exon coordinates projected onto the spliced sequence. + use_star_for_stop_codon: For `tr_cds_fasta_file` option, use '*' instead of '.' as stop codon translation. + ensembl_gtf_to_gff3: Ensembl GTF to GFF3 conversion, adds version to IDs. + chr_replace_map: A name mapping table for converting reference sequence names, + having this 2-column format: . + output_gtf: Main output will be GTF instead of GFF3. Mutually exclusive with `output_bed`, `output_tlf`, `output_table`. + output_bed: Output records in BED format instead of default GFF3. Mutually exclusive with `output_gtf`, `output_tlf`, `output_table`. + output_tlf: Output "transcript line format". Mutually exclusive with `output_gtf`, `output_bed`, `output_table`. + output_table: Output a simple tab delimited format instead of GFF, with columns + having the values of GFF attributes given in . + Mutually exclusive with `output_gtf`, `output_bed`, `output_tlf`. + expose_warnings: Expose (warn about) duplicate transcript IDs and other potential + problems with the given GFF/GTF records. + sort_alphabetically: Chromosomes (reference sequences) are sorted alphabetically. + sort_by_ref_list: Sort the reference sequences by the order in which their + names are given in the file. + + Returns: + A dictionary containing the command executed, stdout, stderr, and a list of output files. + """ + command = ["gffread"] + output_files = [] + + # Input validation and command construction + if input_gff: + if not input_gff.is_file(): + raise FileNotFoundError(f"Input GFF file not found: {input_gff}") + command.append(str(input_gff)) + + if genomic_seqs: + if not genomic_seqs.exists(): # Check if it's a file or directory + raise FileNotFoundError(f"Genomic sequences file or directory not found: {genomic_seqs}") + command.extend(["-g", str(genomic_seqs)]) + + if seq_info_fsize: + if not seq_info_fsize.is_file(): + raise FileNotFoundError(f"Sequence info file not found: {seq_info_fsize}") + command.extend(["-s", str(seq_info_fsize)]) + + if ids_list and nids_list: + raise ValueError("Options --ids and --nids are mutually exclusive.") + if ids_list: + if not ids_list.is_file(): + raise FileNotFoundError(f"IDs list file not found: {ids_list}") + command.extend(["--ids", str(ids_list)]) + if nids_list: + if not nids_list.is_file(): + raise FileNotFoundError(f"NIDs list file not found: {nids_list}") + command.extend(["--nids", str(nids_list)]) + + if coding_only and non_coding_only: + raise ValueError("Options -C (coding only) and --nc (non-coding only) are mutually exclusive.") + + if merge_transcripts and cluster_only: + raise ValueError("Options -M/--merge and --cluster-only are mutually exclusive.") + + output_format_options_count = sum([output_gtf, output_bed, output_tlf, output_table is not None]) + if output_format_options_count > 1: + raise ValueError("Only one output format option (-T, --bed, --tlf, --table) can be specified.") + + # Add parameters to command list + if output_file: + command.extend(["-o", str(output_file)]) + output_files.append(str(output_file)) + + if track_name: + command.extend(["-t", track_name]) + if region: + command.extend(["-r", region]) + if region_fully_contained: + command.append("-R") + if junction_match: + command.extend(["--jmatch", junction_match]) + if no_pseudo: + command.append("--no-pseudo") + + if max_intron is not None: + if max_intron < 0: + raise ValueError("max_intron must be a non-negative integer.") + command.extend(["-i", str(max_intron)]) + if min_length is not None: + if min_length < 0: + raise ValueError("min_length must be a non-negative integer.") + command.extend(["-l", str(min_length)]) + if discard_single_exon: + command.append("-U") + if coding_only: + command.append("-C") + if non_coding_only: + command.append("--nc") + if ignore_locus: + command.append("--ignore-locus") + if add_description_attribute: + if not seq_info_fsize: + raise ValueError("Option -A requires -s to be provided.") + command.append("-A") + + if keep_all_gff_attrs: + command.append("-F") + if keep_exon_attrs: + if not keep_all_gff_attrs: + raise ValueError("--keep-exon-attrs requires -F option.") + command.append("--keep-exon-attrs") + if move_exon_attrs_to_transcript: + command.append("-G") + if output_attrs: + command.extend(["--attrs", output_attrs]) + if keep_genes: + command.append("--keep-genes") + if keep_comments: + command.append("--keep-comments") + if process_other_records: + command.append("-O") + + if discard_in_frame_stop_codons: + if not genomic_seqs: + raise ValueError("Option -V requires -g to be provided.") + command.append("-V") + if adjust_cds_phase: + if not discard_in_frame_stop_codons: + raise ValueError("Option -H requires -V option.") + command.append("-H") + if check_single_exon_opposite_strand: + if not discard_in_frame_stop_codons: + raise ValueError("Option -B requires -V option.") + if not genomic_seqs: # Redundant check, but good for clarity + raise ValueError("Option -B requires -g to be provided.") + command.append("-B") + if add_coding_status_attrs: + if not genomic_seqs: + raise ValueError("Option -P requires -g to be provided.") + command.append("-P") + if add_has_cds_attribute: + command.append("--add-hasCDS") + if adjust_stop_codon: + command.append("--adj-stop") + # --adj-stop implicitly enables -P, which requires -g + if not genomic_seqs: + raise ValueError("Option --adj-stop implicitly enables -P, which requires -g to be provided.") + + if discard_non_canonical_splice_sites: + command.append("-N") + if discard_incomplete_cds: + command.append("-J") + if input_is_bed: + command.append("--in-bed") + if input_is_tlf: + command.append("--in-tlf") + if stream_processing: + command.append("--stream") + + if merge_transcripts: + command.append("-M") + if dup_info_file: + if not merge_transcripts: + raise ValueError("Option -d requires -M/--merge option.") + command.extend(["-d", str(dup_info_file)]) + output_files.append(str(dup_info_file)) + if cluster_only: + command.append("--cluster-only") + + # Options -K, -Q, -Y require -M or --cluster-only + if discard_shorter_contained: + if not (merge_transcripts or cluster_only): + raise ValueError("Option -K requires -M/--merge or --cluster-only option.") + command.append("-K") + if relax_boundary_containment: + if not (merge_transcripts or cluster_only): + raise ValueError("Option -Q requires -M/--merge or --cluster-only option.") + command.append("-Q") + if discard_overlapping_single_exon: + if not (merge_transcripts or cluster_only): + raise ValueError("Option -Y requires -M/--merge or --cluster-only option.") + command.append("-Y") + + if force_exons: + command.append("--force-exons") + if gene_to_exon: + command.append("--gene2exon") + if transcript_adopt_parent: + command.append("--t-adopt") + if decode_url_chars: + command.append("-D") + if merge_close_exons: + command.append("-Z") + if output_junctions: + command.append("-j") + + if exons_fasta_file: + if not genomic_seqs: + raise ValueError("Option -w requires -g to be provided.") + command.extend(["-w", str(exons_fasta_file)]) + output_files.append(str(exons_fasta_file)) + if w_add_bases is not None: + if not exons_fasta_file: + raise ValueError("--w-add requires -w option.") + if w_add_bases < 0: + raise ValueError("w_add_bases must be a non-negative integer.") + command.extend(["--w-add", str(w_add_bases)]) + if w_no_cds_info: + if not exons_fasta_file: + raise ValueError("--w-nocds requires -w option.") + command.append("--w-nocds") + + if cds_fasta_file: + if not genomic_seqs: + raise ValueError("Option -x requires -g to be provided.") + command.extend(["-x", str(cds_fasta_file)]) + output_files.append(str(cds_fasta_file)) + if tr_cds_fasta_file: + if not genomic_seqs: + raise ValueError("Option -y requires -g to be provided.") + command.extend(["-y", str(tr_cds_fasta_file)]) + output_files.append(str(tr_cds_fasta_file)) + + if write_exon_coords_to_defline: + if not (exons_fasta_file or cds_fasta_file or tr_cds_fasta_file): + raise ValueError("Option -W requires at least one of -w, -x, or -y options.") + command.append("-W") + if use_star_for_stop_codon: + if not tr_cds_fasta_file: + raise ValueError("Option -S requires -y option.") + command.append("-S") + + if ensembl_gtf_to_gff3: + command.append("-L") + if chr_replace_map: + if not chr_replace_map.is_file(): + raise FileNotFoundError(f"Chromosome replacement map file not found: {chr_replace_map}") + command.extend(["-m", str(chr_replace_map)]) + + if output_gtf: + command.append("-T") + if output_bed: + command.append("--bed") + if output_tlf: + command.append("--tlf") + if output_table: + command.extend(["--table", output_table]) + + if expose_warnings: + command.append("-E") + + if sort_alphabetically: + command.append("--sort-alpha") + if sort_by_ref_list: + if not sort_by_ref_list.is_file(): + raise FileNotFoundError(f"Reference list file for sorting not found: {sort_by_ref_list}") + command.extend(["--sort-by", str(sort_by_ref_list)]) + + # Execute the command + try: + process = subprocess.run( + command, + check=True, + capture_output=True, + text=True + ) + return { + "command_executed": " ".join(command), + "stdout": process.stdout, + "stderr": process.stderr, + "output_files": [str(p) for p in output_files] + } + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(command), + "stdout": e.stdout, + "stderr": e.stderr, + "error": f"gffread command failed with exit code {e.returncode}: {e}", + "output_files": [] + } + except FileNotFoundError: + return { + "command_executed": " ".join(command), + "stdout": "", + "stderr": "gffread command not found. Please ensure it is installed and in your PATH.", + "error": "gffread command not found.", + "output_files": [] + } diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_gffread/app/gffread_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_gffread/app/gffread_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..ec37b06e3bccdb09b6a1c00d8b2bf5615ab98f3a --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_gffread/app/gffread_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_gffread/app/gffread_server.py') +SERVER_NAME = 'biosci_gffread' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_gffread/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_gffread/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_gffread/app/requirements.txt @@ -0,0 +1 @@ + diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_gffread/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_gffread/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..6f3f529a3ab14b57b391ae29bd92c8abf9c6f2db --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_gffread/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-gffread: + build: . + image: mcp-gffread:latest + container_name: mcp-gffread + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=gffread + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_gffread/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_gffread/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..d6ad926439cb11f969083bde55d9b2f0c0715aae --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_gffread/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - gffread + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_gffread/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_gffread/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_gffread/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_hisat2/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_hisat2/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..0b86be0a97e6c7083f4146fef19a90fd1701373d --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_hisat2/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install hisat2 via conda (e.g., from bioconda) +RUN conda install -c bioconda hisat2 -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY app/hisat2_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/hisat2_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/hisat2_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_hisat2/app/hisat2_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_hisat2/app/hisat2_server.py new file mode 100644 index 0000000000000000000000000000000000000000..9fed5d8a0c4fcaf1d5a9e6456822fe79a9da6a65 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_hisat2/app/hisat2_server.py @@ -0,0 +1,609 @@ +import subprocess +import tempfile +from pathlib import Path +from typing import Optional, List, Dict + +# This is a placeholder for the MCP decorator. +# In a real MCP environment, this would be provided by the MCP framework. +def tool(): + def decorator(f): + return f + return decorator + +mcp = type("mcp", (), {"tool": tool})() + + +@mcp.tool() +def hisat2_align( + index_prefix: Path, + sam_output: Path, + mate1_files: Optional[str] = None, + mate2_files: Optional[str] = None, + unpaired_reads: Optional[str] = None, + input_format: str = "fastq", + skip: int = 0, + upto: Optional[int] = None, + trim5: int = 0, + trim3: int = 0, + quality_encoding: str = "phred33", + max_seed_mismatches: int = 0, + seed_length: int = 20, + seed_interval: str = "S,1,0.50", + n_ceil: str = "L,0,0.15", + pen_cansplice: int = 0, + pen_noncansplice: int = 12, + pen_canintronlen: str = "G,-8,1", + pen_noncanintronlen: str = "G,-8,1", + min_intronlen: int = 20, + max_intronlen: int = 500000, + known_splicesite_infile: Optional[Path] = None, + novel_splicesite_outfile: Optional[Path] = None, + novel_splicesite_infile: Optional[Path] = None, + no_temp_splicesite: bool = False, + no_softclip: bool = False, + sp: str = "2,1", + mismatch_penalty: str = "6,2", + np: int = 1, + read_gap_penalty: str = "5,3", + ref_gap_penalty: str = "5,3", + score_min: str = "L,-0.6,-0.6", + k: Optional[int] = None, + report_all: bool = False, + secondary: bool = False, + min_frag_len: int = 0, + max_frag_len: int = 500, + mate_orientation: Optional[str] = None, + no_mixed: bool = False, + no_discordant: bool = False, + summary_file: Optional[Path] = None, + new_summary: bool = False, + met_file: Optional[Path] = None, + met_stderr: bool = False, + met_interval: int = 1, + unaligned_reads_file: Optional[Path] = None, + aligned_reads_file: Optional[Path] = None, + unaligned_concordant_reads_file: Optional[Path] = None, + aligned_concordant_reads_file: Optional[Path] = None, + quiet: bool = False, + no_head: bool = False, + no_sq: bool = False, + rg_id: Optional[str] = None, + rg: Optional[List[str]] = None, + remove_chrname: bool = False, + add_chrname: bool = False, + dta: bool = False, + dta_cufflinks: bool = False, + no_spliced_alignment: bool = False, + rna_strandness: Optional[str] = None, + transcriptome_mapping_only: bool = False, + bwa_mem: bool = False, + threads: int = 1, + reorder: bool = False, + mm: bool = False, + qc_filter: bool = False, + seed: int = 0, + non_deterministic: bool = False, +) -> Dict: + """ + Aligns sequencing reads to a reference genome using HISAT2. + + This function wraps the main 'hisat2' alignment command, providing a comprehensive + interface to its numerous options for input, alignment, scoring, paired-end, + output, and performance tuning. + """ + # Input validation + if not (mate1_files and mate2_files) and not unpaired_reads: + raise ValueError("Either paired-end reads (-1 and -2) or unpaired reads (-U) must be provided.") + if (mate1_files and not mate2_files) or (not mate1_files and mate2_files): + raise ValueError("For paired-end alignment, both -1 and -2 must be specified.") + + valid_formats = ["fastq", "fasta", "raw"] + if input_format not in valid_formats: + raise ValueError(f"Invalid input_format '{input_format}'. Must be one of {valid_formats}.") + + valid_qualities = ["phred33", "phred64", "solexa-quals"] + if quality_encoding not in valid_qualities: + raise ValueError(f"Invalid quality_encoding '{quality_encoding}'. Must be one of {valid_qualities}.") + + if mate_orientation and mate_orientation not in ["fr", "rf", "ff"]: + raise ValueError("Invalid mate_orientation. Must be one of 'fr', 'rf', 'ff'.") + + if rna_strandness and rna_strandness not in ["F", "R", "FR", "RF"]: + raise ValueError("Invalid rna_strandness. Must be one of 'F', 'R', 'FR', 'RF'.") + + if not index_prefix.parent.exists(): + raise FileNotFoundError(f"Index directory not found: {index_prefix.parent}") + + cmd = ["hisat2"] + + # Input options + cmd.extend(["-x", str(index_prefix)]) + if mate1_files: + cmd.extend(["-1", mate1_files]) + if mate2_files: + cmd.extend(["-2", mate2_files]) + if unpaired_reads: + cmd.extend(["-U", unpaired_reads]) + + if input_format == "fasta": + cmd.append("-f") + elif input_format == "raw": + cmd.append("-r") + else: # fastq is default + cmd.append("-q") + + if skip > 0: + cmd.extend(["-s", str(skip)]) + if upto is not None: + cmd.extend(["-u", str(upto)]) + if trim5 > 0: + cmd.extend(["-5", str(trim5)]) + if trim3 > 0: + cmd.extend(["-3", str(trim3)]) + + cmd.append(f"--{quality_encoding}") + + # Alignment options + cmd.extend(["-N", str(max_seed_mismatches)]) + cmd.extend(["-L", str(seed_length)]) + cmd.extend(["-i", seed_interval]) + cmd.extend(["--n-ceil", n_ceil]) + cmd.extend(["--pen-cansplice", str(pen_cansplice)]) + cmd.extend(["--pen-noncansplice", str(pen_noncansplice)]) + cmd.extend(["--pen-canintronlen", pen_canintronlen]) + cmd.extend(["--pen-noncanintronlen", pen_noncanintronlen]) + cmd.extend(["--min-intronlen", str(min_intronlen)]) + cmd.extend(["--max-intronlen", str(max_intronlen)]) + if known_splicesite_infile: + cmd.extend(["--known-splicesite-infile", str(known_splicesite_infile)]) + if novel_splicesite_outfile: + cmd.extend(["--novel-splicesite-outfile", str(novel_splicesite_outfile)]) + if novel_splicesite_infile: + cmd.extend(["--novel-splicesite-infile", str(novel_splicesite_infile)]) + if no_temp_splicesite: + cmd.append("--no-temp-splicesite") + if no_softclip: + cmd.append("--no-softclip") + cmd.extend(["--sp", sp]) + + # Scoring options + cmd.extend(["--mp", mismatch_penalty]) + cmd.extend(["--np", str(np)]) + cmd.extend(["--rdg", read_gap_penalty]) + cmd.extend(["--rfg", ref_gap_penalty]) + cmd.extend(["--score-min", score_min]) + + # Reporting options + if k is not None: + cmd.extend(["-k", str(k)]) + if report_all: + cmd.append("--all") + if secondary: + cmd.append("--secondary") + + # Paired-end options + if mate1_files: + cmd.extend(["-I", str(min_frag_len)]) + cmd.extend(["-X", str(max_frag_len)]) + if mate_orientation: + cmd.append(f"--{mate_orientation}") + if no_mixed: + cmd.append("--no-mixed") + if no_discordant: + cmd.append("--no-discordant") + + # Output options + cmd.extend(["-S", str(sam_output)]) + if summary_file: + cmd.extend(["--summary-file", str(summary_file)]) + if new_summary: + cmd.append("--new-summary") + if met_file: + cmd.extend(["--met-file", str(met_file)]) + if met_stderr: + cmd.append("--met-stderr") + cmd.extend(["--met", str(met_interval)]) + if unaligned_reads_file: + cmd.extend(["--un", str(unaligned_reads_file)]) + if aligned_reads_file: + cmd.extend(["--al", str(aligned_reads_file)]) + if unaligned_concordant_reads_file: + cmd.extend(["--un-conc", str(unaligned_concordant_reads_file)]) + if aligned_concordant_reads_file: + cmd.extend(["--al-conc", str(aligned_concordant_reads_file)]) + if quiet: + cmd.append("--quiet") + if no_head: + cmd.append("--no-head") + if no_sq: + cmd.append("--no-sq") + if rg_id: + cmd.extend(["--rg-id", rg_id]) + if rg: + for r in rg: + cmd.extend(["--rg", r]) + if remove_chrname: + cmd.append("--remove-chrname") + if add_chrname: + cmd.append("--add-chrname") + if dta: + cmd.append("--dta") + if dta_cufflinks: + cmd.append("--dta-cufflinks") + if no_spliced_alignment: + cmd.append("--no-spliced-alignment") + if rna_strandness: + cmd.extend(["--rna-strandness", rna_strandness]) + if transcriptome_mapping_only: + cmd.append("--transcriptome-mapping-only") + if bwa_mem: + cmd.append("--bwa-mem") + + # Performance options + cmd.extend(["-p", str(threads)]) + if reorder: + cmd.append("--reorder") + if mm: + cmd.append("--mm") + if qc_filter: + cmd.append("--qc-filter") + cmd.extend(["--seed", str(seed)]) + if non_deterministic: + cmd.append("--non-deterministic") + + command_str = " ".join(cmd) + output_files = {"sam_output": str(sam_output)} + + try: + result = subprocess.run(cmd, capture_output=True, text=True, check=True) + # Add other potential output files to the list + if summary_file: output_files["summary_file"] = str(summary_file) + if met_file: output_files["met_file"] = str(met_file) + if unaligned_reads_file: output_files["unaligned_reads_file"] = str(unaligned_reads_file) + if aligned_reads_file: output_files["aligned_reads_file"] = str(aligned_reads_file) + if unaligned_concordant_reads_file: output_files["unaligned_concordant_reads_file"] = str(unaligned_concordant_reads_file) + if aligned_concordant_reads_file: output_files["aligned_concordant_reads_file"] = str(aligned_concordant_reads_file) + if novel_splicesite_outfile: output_files["novel_splicesite_outfile"] = str(novel_splicesite_outfile) + + return { + "command_executed": command_str, + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": output_files, + } + except subprocess.CalledProcessError as e: + return { + "command_executed": command_str, + "stdout": e.stdout, + "stderr": e.stderr, + "error": f"HISAT2 alignment failed with exit code {e.returncode}", + "output_files": {}, + } + +@mcp.tool() +def hisat2_build( + reference_in: str, + index_basename: str, + large_index: bool = False, + no_auto: bool = False, + packed: bool = False, + bmax: Optional[int] = None, + bmaxdivn: int = 4, + dcv: int = 1024, + no_dc: bool = False, + no_ref: bool = False, + just_ac: bool = False, + offrate: int = 5, + ftabchars: int = 10, + local_offrate: int = 3, + local_ftabchars: int = 6, + snp_file: Optional[Path] = None, + haplotype_file: Optional[Path] = None, + ss_file: Optional[Path] = None, + exon_file: Optional[Path] = None, + threads: int = 1, + seed: int = 0, + quiet: bool = False, +) -> Dict: + """ + Builds a HISAT2 index from a set of reference sequences. + + This function wraps the 'hisat2-build' command, which constructs the index files + required by the main 'hisat2' aligner. + """ + # Input validation + # Assuming reference_in is a comma-separated list of file paths + for ref_path_str in reference_in.split(','): + if not Path(ref_path_str).exists(): + raise FileNotFoundError(f"Reference FASTA file not found: {ref_path_str}") + + cmd = ["hisat2-build"] + + # Options + if large_index: + cmd.append("--large-index") + if no_auto: + cmd.append("--noauto") + if packed: + cmd.append("--packed") + if bmax is not None: + cmd.extend(["--bmax", str(bmax)]) + cmd.extend(["--bmaxdivn", str(bmaxdivn)]) + cmd.extend(["--dcv", str(dcv)]) + if no_dc: + cmd.append("--nodc") + if no_ref: + cmd.append("--noref") + if just_ac: + cmd.append("--just-ac") + cmd.extend(["--offrate", str(offrate)]) + cmd.extend(["--ftabchars", str(ftabchars)]) + cmd.extend(["--localoffrate", str(local_offrate)]) + cmd.extend(["--localftabchars", str(local_ftabchars)]) + if snp_file: + cmd.extend(["--snp", str(snp_file)]) + if haplotype_file: + cmd.extend(["--haplotype", str(haplotype_file)]) + if ss_file: + cmd.extend(["--ss", str(ss_file)]) + if exon_file: + cmd.extend(["--exon", str(exon_file)]) + cmd.extend(["-p", str(threads)]) + cmd.extend(["--seed", str(seed)]) + if quiet: + cmd.append("--quiet") + + # Positional arguments + cmd.extend([reference_in, index_basename]) + + command_str = " ".join(cmd) + + try: + result = subprocess.run(cmd, capture_output=True, text=True, check=True) + + # Find created index files + output_dir = Path(index_basename).parent + base = Path(index_basename).name + index_files = [str(p) for p in output_dir.glob(f"{base}*.ht2")] + + return { + "command_executed": command_str, + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": {"index_files": index_files}, + } + except subprocess.CalledProcessError as e: + return { + "command_executed": command_str, + "stdout": e.stdout, + "stderr": e.stderr, + "error": f"HISAT2 build failed with exit code {e.returncode}", + "output_files": {}, + } + +@mcp.tool() +def hisat2_inspect( + index_basename: str, + names: bool = False, + summary: bool = False, + snp: bool = False, + ss: bool = False, + haplotype: bool = False, + extract: Optional[str] = None, + across: Optional[int] = None, + verbose: bool = False, +) -> Dict: + """ + Inspects a HISAT2 index to extract information. + + This function wraps the 'hisat2-inspect' command. It can print reference sequence + names, a summary of the index, or extract specific sequences. + """ + # Input validation + modes = [names, summary, snp, ss, haplotype, extract is not None] + if sum(modes) > 1: + raise ValueError("Only one inspection mode (--names, --summary, --snp, --ss, --haplotype, --extract) can be active at a time.") + if sum(modes) == 0: + raise ValueError("At least one inspection mode must be selected.") + + if not Path(f"{index_basename}.1.ht2").exists(): + raise FileNotFoundError(f"Index file not found for basename: {index_basename}") + + cmd = ["hisat2-inspect"] + + if names: + cmd.append("--names") + if summary: + cmd.append("--summary") + if snp: + cmd.append("--snp") + if ss: + cmd.append("--ss") + if haplotype: + cmd.append("--haplotype") + if extract: + cmd.extend(["--extract", extract]) + if across is not None: + cmd.extend(["--across", str(across)]) + if verbose: + cmd.append("--verbose") + + cmd.append(index_basename) + command_str = " ".join(cmd) + + try: + result = subprocess.run(cmd, capture_output=True, text=True, check=True) + return { + "command_executed": command_str, + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": {}, + } + except subprocess.CalledProcessError as e: + return { + "command_executed": command_str, + "stdout": e.stdout, + "stderr": e.stderr, + "error": f"HISAT2 inspect failed with exit code {e.returncode}", + "output_files": {}, + } + +@mcp.tool() +def hisat2_extract_splice_sites( + gtf_file: Path, + output_file: Path, +) -> Dict: + """ + Extracts splice sites from a GTF file for use with hisat2-build. + + This function wraps the 'hisat2_extract_splice_sites.py' utility script. + """ + if not gtf_file.exists(): + raise FileNotFoundError(f"Input GTF file not found: {gtf_file}") + + cmd = ["hisat2_extract_splice_sites.py", str(gtf_file)] + command_str = " ".join(cmd) + f" > {output_file}" + + try: + with open(output_file, "w") as f_out: + result = subprocess.run(cmd, stdout=f_out, stderr=subprocess.PIPE, text=True, check=True) + + return { + "command_executed": command_str, + "stdout": "", # stdout is redirected to file + "stderr": result.stderr, + "output_files": {"splice_sites": str(output_file)}, + } + except FileNotFoundError: + return { + "command_executed": command_str, + "stdout": "", + "stderr": "Error: 'hisat2_extract_splice_sites.py' not found. Make sure the HISAT2 scripts are in your PATH.", + "error": "Executable not found", + "output_files": {}, + } + except subprocess.CalledProcessError as e: + return { + "command_executed": command_str, + "stdout": e.stdout or "", + "stderr": e.stderr or "", + "error": f"Script failed with exit code {e.returncode}", + "output_files": {}, + } + +@mcp.tool() +def hisat2_extract_exons( + gtf_file: Path, + output_file: Path, +) -> Dict: + """ + Extracts exons from a GTF file for use with hisat2-build. + + This function wraps the 'hisat2_extract_exons.py' utility script. + """ + if not gtf_file.exists(): + raise FileNotFoundError(f"Input GTF file not found: {gtf_file}") + + cmd = ["hisat2_extract_exons.py", str(gtf_file)] + command_str = " ".join(cmd) + f" > {output_file}" + + try: + with open(output_file, "w") as f_out: + result = subprocess.run(cmd, stdout=f_out, stderr=subprocess.PIPE, text=True, check=True) + + return { + "command_executed": command_str, + "stdout": "", # stdout is redirected to file + "stderr": result.stderr, + "output_files": {"exons": str(output_file)}, + } + except FileNotFoundError: + return { + "command_executed": command_str, + "stdout": "", + "stderr": "Error: 'hisat2_extract_exons.py' not found. Make sure the HISAT2 scripts are in your PATH.", + "error": "Executable not found", + "output_files": {}, + } + except subprocess.CalledProcessError as e: + return { + "command_executed": command_str, + "stdout": e.stdout or "", + "stderr": e.stderr or "", + "error": f"Script failed with exit code {e.returncode}", + "output_files": {}, + } + +@mcp.tool() +def hisat2_simulate_reads( + reference_fasta: Path, + num_reads: int, + output_prefix: str, + read_len: int = 100, + error_rate: float = 0.01, + max_mismatches: int = 4, + insert_size: int = 300, + insert_stdev: int = 30, + paired_end: bool = False, + quiet: bool = False, +) -> Dict: + """ + Simulates sequencing reads from a reference genome. + + This function wraps the 'hisat2_simulate_reads.py' utility script. + """ + if not reference_fasta.exists(): + raise FileNotFoundError(f"Reference FASTA file not found: {reference_fasta}") + if num_reads <= 0: + raise ValueError("Number of reads must be positive.") + + cmd = [ + "hisat2_simulate_reads.py", + str(reference_fasta), + str(num_reads), + "--output-prefix", output_prefix, + "--read-len", str(read_len), + "--error-rate", str(error_rate), + "--max-mismatches", str(max_mismatches), + "--insert-size", str(insert_size), + "--insert-stdev", str(insert_stdev), + ] + if paired_end: + cmd.append("--paired-end") + if quiet: + cmd.append("--quiet") + + command_str = " ".join(cmd) + + try: + result = subprocess.run(cmd, capture_output=True, text=True, check=True) + + output_files = {} + if paired_end: + output_files["mate1"] = f"{output_prefix}.1.fq" + output_files["mate2"] = f"{output_prefix}.2.fq" + else: + output_files["reads"] = f"{output_prefix}.fq" + + return { + "command_executed": command_str, + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": output_files, + } + except FileNotFoundError: + return { + "command_executed": command_str, + "stdout": "", + "stderr": "Error: 'hisat2_simulate_reads.py' not found. Make sure the HISAT2 scripts are in your PATH.", + "error": "Executable not found", + "output_files": {}, + } + except subprocess.CalledProcessError as e: + return { + "command_executed": command_str, + "stdout": e.stdout, + "stderr": e.stderr, + "error": f"Script failed with exit code {e.returncode}", + "output_files": {}, + } \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_hisat2/app/hisat2_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_hisat2/app/hisat2_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..3b82282bc498ddbe2e5b0a769a9be661f1cf95f0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_hisat2/app/hisat2_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_hisat2/app/hisat2_server.py') +SERVER_NAME = 'biosci_hisat2' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_hisat2/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_hisat2/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_hisat2/app/requirements.txt @@ -0,0 +1 @@ + diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_hisat2/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_hisat2/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..5745e33428aaa7cfe9b6f75dda93d76d14a6ff54 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_hisat2/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-hisat2: + build: . + image: mcp-hisat2:latest + container_name: mcp-hisat2 + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=hisat2 + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_hisat2/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_hisat2/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..b8e575dd31bac67b89dcbfe6a17b1744837fdfe4 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_hisat2/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - hisat2 + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_hisat2/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_hisat2/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_hisat2/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_insilicoseq/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_insilicoseq/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..d1b7081a6afa7e0a7f9622dc5810dd6c55b9ba9e --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_insilicoseq/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install insilicoseq via conda (e.g., from bioconda) +RUN conda install -c bioconda insilicoseq -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY app/insilicoseq_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/insilicoseq_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/insilicoseq_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_insilicoseq/app/insilicoseq_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_insilicoseq/app/insilicoseq_server.py new file mode 100644 index 0000000000000000000000000000000000000000..34ac457f4caed2c426ff5e7fd9f0380f65c439fc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_insilicoseq/app/insilicoseq_server.py @@ -0,0 +1,237 @@ +import subprocess +import tempfile +from pathlib import Path +from typing import List, Optional + +# @mcp.tool() - This decorator is commented out as per the instructions. +def generate( + output: str, + genomes: Optional[List[Path]] = None, + ncbi: Optional[str] = None, + ncbi_n_genomes: int = 10, + n_reads: str = "1m", + model: str = "miseq", + abundance: Optional[str] = None, + coverage: Optional[str] = None, + draft: Optional[Path] = None, + gc_model: Optional[Path] = None, + mode: Optional[str] = "kde", + seed: Optional[int] = None, + cpus: int = 1, + compress: bool = False, + perfect: bool = False, + quiet: bool = False, +) -> dict: + """ + Generate simulated Illumina reads from reference genomes. + + This tool simulates sequencing reads, primarily for metagenomic samples, + but can also be used for single genomes. It supports various error models, + GC bias, and abundance distributions. + + Args: + output (str): Prefix for the output files. + genomes (Optional[List[Path]]): List of FASTA files containing reference genomes. + Mutually exclusive with 'ncbi'. + ncbi (Optional[str]): Download random genomes from NCBI. Provide a kingdom + (e.g., 'bacteria', 'viruses', 'archaea'). + Mutually exclusive with 'genomes'. + ncbi_n_genomes (int): Number of genomes to download from NCBI if 'ncbi' is used. + n_reads (str): Total number of reads to generate. Suffixes 'k', 'm', 'g' are supported (e.g., '10k', '1m'). + model (str): Error model to use. Can be a built-in model ('miseq', 'hiseq', 'novaseq') + or a path to a custom '.npz' model file. + abundance (Optional[str]): Abundance distribution. Can be a distribution name + ('uniform', 'lognormal', 'halfnormal', 'zero_inflated_lognormal') + or a path to an abundance file. Mutually exclusive with 'coverage'. + coverage (Optional[str]): Coverage distribution. Can be a distribution name or a path + to a coverage file. Mutually exclusive with 'abundance'. + draft (Optional[Path]): Path to a file indicating which sequences are draft quality. + gc_model (Optional[Path]): Path to a custom GC bias model file. + mode (Optional[str]): Sequencing simulation mode. Defaults to 'kde'. + seed (Optional[int]): Random seed for reproducibility. + cpus (int): Number of CPUs to use for simulation. + compress (bool): If True, compress the output FASTQ files with gzip. + perfect (bool): If True, generate perfect reads without sequencing errors. + quiet (bool): If True, suppress progress and informational messages. + + Returns: + dict: A dictionary containing the executed command, stdout, stderr, + and a list of generated output files. + """ + # Input validation + if not ((genomes is not None) ^ (ncbi is not None)): + raise ValueError("Either 'genomes' or 'ncbi' must be provided, but not both.") + + if abundance is not None and coverage is not None: + raise ValueError("'abundance' and 'coverage' are mutually exclusive.") + + if cpus <= 0: + raise ValueError("'cpus' must be a positive integer.") + + if ncbi_n_genomes <= 0: + raise ValueError("'ncbi_n_genomes' must be a positive integer.") + + cmd = ["iss", "generate", "--output", output] + + if genomes: + for genome_file in genomes: + if not genome_file.is_file(): + raise FileNotFoundError(f"Genome file not found: {genome_file}") + cmd.extend(["--genomes", str(genome_file)]) + elif ncbi: + cmd.extend(["--ncbi", ncbi, "--n_genomes", str(ncbi_n_genomes)]) + + cmd.extend(["--n_reads", n_reads]) + + if model.endswith(".npz"): + model_path = Path(model) + if not model_path.is_file(): + raise FileNotFoundError(f"Custom model file not found: {model_path}") + cmd.extend(["--model", model]) + + if abundance: + try: + abundance_path = Path(abundance) + if not abundance_path.is_file(): + # It's a distribution name, not a file + pass + except (TypeError, ValueError): + pass # It's a distribution name + cmd.extend(["--abundance", abundance]) + + if coverage: + try: + coverage_path = Path(coverage) + if not coverage_path.is_file(): + # It's a distribution name, not a file + pass + except (TypeError, ValueError): + pass # It's a distribution name + cmd.extend(["--coverage", coverage]) + + if draft: + if not draft.is_file(): + raise FileNotFoundError(f"Draft file not found: {draft}") + cmd.extend(["--draft", str(draft)]) + + if gc_model: + if not gc_model.is_file(): + raise FileNotFoundError(f"GC model file not found: {gc_model}") + cmd.extend(["--gc_model", str(gc_model)]) + + if mode: + cmd.extend(["--mode", mode]) + + if seed is not None: + cmd.extend(["--seed", str(seed)]) + + cmd.extend(["--cpus", str(cpus)]) + + if compress: + cmd.append("--compress") + if perfect: + cmd.append("--perfect") + if quiet: + cmd.append("--quiet") + + try: + result = subprocess.run( + cmd, + capture_output=True, + text=True, + check=True, + ) + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(cmd), + "stdout": e.stdout, + "stderr": e.stderr, + "error": f"InSilicoSeq generate failed with exit code {e.returncode}", + "output_files": [] + } + + # Define output files + ext = ".fastq.gz" if compress else ".fastq" + output_files = [ + f"{output}_R1{ext}", + f"{output}_R2{ext}", + f"{output}_abundance.txt", + ] + + return { + "command_executed": " ".join(cmd), + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": output_files, + } + + +# @mcp.tool() - This decorator is commented out as per the instructions. +def model( + bam: Path, + output: str, + cpus: int = 1, + quiet: bool = False, +) -> dict: + """ + Create a sequencing error model from an aligned BAM file. + + This tool analyzes a BAM file containing reads aligned to a reference genome + to build a statistical model of sequencing errors, which can then be used + by the 'generate' tool. + + Args: + bam (Path): Path to the input sorted and indexed BAM file. + output (str): Prefix for the output model file. A '.npz' extension will be added. + cpus (int): Number of CPUs to use for model creation. + quiet (bool): If True, suppress progress and informational messages. + + Returns: + dict: A dictionary containing the executed command, stdout, stderr, + and the path to the generated model file. + """ + # Input validation + if not bam.is_file(): + raise FileNotFoundError(f"BAM file not found: {bam}") + + # Check for BAM index file + bai_file_1 = bam.with_suffix(".bam.bai") + bai_file_2 = bam.with_suffix(".bai") + if not (bai_file_1.is_file() or bai_file_2.is_file()): + raise FileNotFoundError( + f"BAM index file (.bai) not found for {bam}. Please index the BAM file." + ) + + if cpus <= 0: + raise ValueError("'cpus' must be a positive integer.") + + cmd = ["iss", "model", "--bam", str(bam), "--output", output] + cmd.extend(["--cpus", str(cpus)]) + + if quiet: + cmd.append("--quiet") + + try: + result = subprocess.run( + cmd, + capture_output=True, + text=True, + check=True, + ) + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(cmd), + "stdout": e.stdout, + "stderr": e.stderr, + "error": f"InSilicoSeq model failed with exit code {e.returncode}", + "output_files": [] + } + + output_model_file = f"{output}.npz" + + return { + "command_executed": " ".join(cmd), + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": [output_model_file], + } \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_insilicoseq/app/insilicoseq_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_insilicoseq/app/insilicoseq_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..c65259203954c5c7af3f86f11412ddcf34b8eb8b --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_insilicoseq/app/insilicoseq_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_insilicoseq/app/insilicoseq_server.py') +SERVER_NAME = 'biosci_insilicoseq' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_insilicoseq/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_insilicoseq/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_insilicoseq/app/requirements.txt @@ -0,0 +1 @@ + diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_insilicoseq/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_insilicoseq/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..eb79cea3b04e786ce93b5d14a7cefdb4d2f38c36 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_insilicoseq/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-insilicoseq: + build: . + image: mcp-insilicoseq:latest + container_name: mcp-insilicoseq + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=insilicoseq + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_insilicoseq/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_insilicoseq/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..6fe4420616b68a726f5134e0090beaceb4fe1686 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_insilicoseq/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - insilicoseq + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_insilicoseq/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_insilicoseq/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_insilicoseq/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_kallisto/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_kallisto/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..be0ff34dfe4e8ccad8b8a12f7c1db222cd600347 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_kallisto/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install kallisto via conda (e.g., from bioconda) +RUN conda install -c bioconda kallisto -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY app/kallisto_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/kallisto_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/kallisto_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_kallisto/app/__pycache__/kallisto_server.cpython-313.pyc b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_kallisto/app/__pycache__/kallisto_server.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..01aa6ba715003262da4228f6ae6fa384ce483d7f Binary files /dev/null and b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_kallisto/app/__pycache__/kallisto_server.cpython-313.pyc differ diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_kallisto/app/kallisto_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_kallisto/app/kallisto_server.py new file mode 100644 index 0000000000000000000000000000000000000000..b89f523a7e8dafc49977479517c1355a3b1bf419 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_kallisto/app/kallisto_server.py @@ -0,0 +1,456 @@ +import subprocess +from pathlib import Path +from typing import List, Optional, Union + +@mcp.tool() +def kallisto_index( + fasta_files: List[str], + index: str, + kmer_size: int = 31, + d_list: Optional[str] = None, + make_unique: bool = False, + aa: bool = False, + distinguish: bool = False, + threads: int = 1, + min_size: Optional[int] = None, + ec_max_size: Optional[int] = None, +): + """ + Builds a kallisto index from a FASTA formatted file of target sequences. + + :param fasta_files: List of FASTA files (plaintext or gzipped) to build the index from. + :param index: Filename for the kallisto index to be constructed. + :param kmer_size: k-mer (odd) length (default: 31, max value: 31). + :param d_list: Path to a FASTA-file containing sequences to mask from quantification. + :param make_unique: Replace repeated target names with unique names. + :param aa: Generate index from a FASTA-file containing amino acid sequences. + :param distinguish: Generate index where sequences are distinguished by the sequence name. + :param threads: Number of threads to use (default: 1). + :param min_size: Length of minimizers (default: automatically chosen). + :param ec_max_size: Maximum number of targets in an equivalence class (default: no maximum). + """ + # Validation + if kmer_size > 31 or kmer_size % 2 == 0: + return {"error": "kmer_size must be an odd integer <= 31"} + + for f in fasta_files: + if not Path(f).exists(): + return {"error": f"Input FASTA file not found: {f}"} + + # kallisto index is single-threaded in common releases and does not accept -t. + # Keep the threads parameter for API compatibility, but do not pass it here. + cmd = ["kallisto", "index", "-i", index, "-k", str(kmer_size)] + + if d_list: + cmd.extend(["-d", d_list]) + if make_unique: + cmd.append("--make-unique") + if aa: + cmd.append("--aa") + if distinguish: + cmd.append("--distinguish") + if min_size is not None: + cmd.extend(["-m", str(min_size)]) + if ec_max_size is not None: + cmd.extend(["-e", str(ec_max_size)]) + + cmd.extend(fasta_files) + + try: + result = subprocess.run(cmd, capture_output=True, text=True, check=True) + return { + "command_executed": " ".join(cmd), + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": [index] + } + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(cmd), + "error": str(e), + "stdout": e.stdout, + "stderr": e.stderr + } + +@mcp.tool() +def kallisto_quant( + fastq_files: List[str], + index: str, + output_dir: str, + bootstrap_samples: int = 0, + seed: int = 42, + plaintext: bool = False, + single: bool = False, + single_overhang: bool = False, + fr_stranded: bool = False, + rf_stranded: bool = False, + fragment_length: Optional[float] = None, + sd: Optional[float] = None, + threads: int = 1, + bias: bool = False, + fusion: bool = False, + pseudobam: bool = False, + genomebam: bool = False, + gtf: Optional[str] = None, + chromosomes: Optional[str] = None +): + """ + Runs the kallisto quantification algorithm on RNA-Seq reads. + + :param fastq_files: List of FASTQ files. For paired-end, provide pairs (e.g., [R1, R2]). + :param index: Filename for the kallisto index to be used. + :param output_dir: Directory to write output to. + :param bootstrap_samples: Number of bootstrap samples (default: 0). + :param seed: Seed for the bootstrap sampling (default: 42). + :param plaintext: Output plaintext instead of HDF5. + :param single: Quantify single-end reads. + :param single_overhang: Include reads where unobserved rest of fragment is predicted to lie outside a transcript. + :param fr_stranded: Strand specific reads, first read forward. + :param rf_stranded: Strand specific reads, first read reverse. + :param fragment_length: Estimated average fragment length (Required for --single). + :param sd: Estimated standard deviation of fragment length (Required for --single). + :param threads: Number of threads to use (default: 1). + :param bias: Learn parameters for a model of sequences specific bias (Legacy/0.48.0). + :param fusion: Look for reads from fusion genes (Legacy/0.48.0). + :param pseudobam: Output pseudoalignments to a BAM file (Legacy/0.48.0). + :param genomebam: Project transcript alignments to genome coordinates (Legacy/0.48.0). + :param gtf: GTF file for genomebam (Required if --genomebam is used). + :param chromosomes: Length of genomic chromosomes for BAM header. + """ + # Validation + if not Path(index).exists(): + return {"error": f"Index file not found: {index}"} + + if single: + if fragment_length is None or sd is None: + return {"error": "fragment_length (-l) and sd (-s) are required when using --single"} + + if genomebam and not gtf: + return {"error": "GTF file (--gtf) is required when using --genomebam"} + + Path(output_dir).mkdir(parents=True, exist_ok=True) + + cmd = ["kallisto", "quant", "-i", index, "-o", output_dir, "-b", str(bootstrap_samples), "--seed", str(seed), "-t", str(threads)] + + if plaintext: cmd.append("--plaintext") + if single: cmd.append("--single") + if single_overhang: cmd.append("--single-overhang") + if fr_stranded: cmd.append("--fr-stranded") + if rf_stranded: cmd.append("--rf-stranded") + if fragment_length is not None: cmd.extend(["-l", str(fragment_length)]) + if sd is not None: cmd.extend(["-s", str(sd)]) + if bias: cmd.append("--bias") + if fusion: cmd.append("--fusion") + if pseudobam: cmd.append("--pseudobam") + if genomebam: cmd.append("--genomebam") + if gtf: cmd.extend(["--gtf", gtf]) + if chromosomes: cmd.extend(["--chromosomes", chromosomes]) + + cmd.extend(fastq_files) + + try: + result = subprocess.run(cmd, capture_output=True, text=True, check=True) + return { + "command_executed": " ".join(cmd), + "stdout": result.stdout, + "stderr": result.stderr, + "output_directory": output_dir + } + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(cmd), + "error": str(e), + "stdout": e.stdout, + "stderr": e.stderr + } + +@mcp.tool() +def kallisto_bus( + fastq_files: List[str], + index: str, + output_dir: str, + technology: str, + batch: Optional[str] = None, + threads: int = 1, + bam: bool = False, + num: bool = False, + num_reads: Optional[int] = None, + tag: Optional[str] = None, + fr_stranded: bool = False, + rf_stranded: bool = False, + unstranded: bool = False, + paired: bool = False, + long_reads: bool = False, + aa: bool = False, + inleaved: bool = False, + batch_barcodes: bool = False, + verbose: bool = False +): + """ + Generates BUS files for single-cell sequencing data. + + :param fastq_files: List of FASTQ files. + :param index: Filename for the kallisto index to be used for pseudoalignment. + :param output_dir: Directory to write output to. + :param technology: Single-cell technology used (e.g., 10xv2, 10xv3, or custom format). + :param batch: Process files listed in a batch file. + :param threads: Number of threads to use (default: 1). + :param bam: Input file is a BAM file. + :param num: Output number of read in flag column (incompatible with --bam). + :param num_reads: Maximum number of reads to process from supplied input. + :param tag: 5' tag sequence to identify UMI reads for certain technologies. + :param fr_stranded: Strand specific reads for UMI-tagged reads, first read forward. + :param rf_stranded: Strand specific reads for UMI-tagged reads, first read reverse. + :param unstranded: Treat all read as non-strand-specific. + :param paired: Treat reads as paired. + :param long_reads: Treat reads as long. + :param aa: Align to index generated from amino acid sequences. + :param inleaved: Specifies that input is an interleaved FASTQ file. + :param batch_barcodes: Records both batch and extracted barcode in BUS file. + :param verbose: Print out progress information every 1M processed reads. + """ + if not Path(index).exists(): + return {"error": f"Index file not found: {index}"} + + Path(output_dir).mkdir(parents=True, exist_ok=True) + + cmd = ["kallisto", "bus", "-i", index, "-o", output_dir, "-x", technology, "-t", str(threads)] + + if batch: cmd.extend(["-B", batch]) + if bam: cmd.append("-b") + if num: cmd.append("-n") + if num_reads is not None: cmd.extend(["-N", str(num_reads)]) + if tag: cmd.extend(["-T", tag]) + if fr_stranded: cmd.append("--fr-stranded") + if rf_stranded: cmd.append("--rf-stranded") + if unstranded: cmd.append("--unstranded") + if paired: cmd.append("--paired") + if long_reads: cmd.append("--long") + if aa: cmd.append("--aa") + if inleaved: cmd.append("--inleaved") + if batch_barcodes: cmd.append("--batch-barcodes") + if verbose: cmd.append("--verbose") + + cmd.extend(fastq_files) + + try: + result = subprocess.run(cmd, capture_output=True, text=True, check=True) + return { + "command_executed": " ".join(cmd), + "stdout": result.stdout, + "stderr": result.stderr, + "output_directory": output_dir + } + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(cmd), + "error": str(e), + "stdout": e.stdout, + "stderr": e.stderr + } + +@mcp.tool() +def kallisto_quant_tcc( + output_dir: str, + index: Optional[str] = None, + txnames: Optional[str] = None, + ec_file: Optional[str] = None, + fragment_file: Optional[str] = None, + long_reads: bool = False, + platform: Optional[str] = None, + fragment_length: Optional[float] = None, + sd: Optional[float] = None, + threads: int = 1, + genemap: Optional[str] = None, + gtf: Optional[str] = None, + bootstrap_samples: int = 0, + matrix_to_files: bool = False, + matrix_to_directories: bool = False, + seed: int = 42, + plaintext: bool = False +): + """ + Runs the EM algorithm to produce estimated counts from a transcript-compatibility-counts matrix file. + + :param output_dir: Directory to write output to. + :param index: Filename for the kallisto index (required if txnames not supplied). + :param txnames: File with names of transcripts (required if index not supplied). + :param ec_file: File containing equivalence classes. + :param fragment_file: File containing fragment length distribution. + :param long_reads: Use version of EM for long reads. + :param platform: [PacBio or ONT] used for sequencing. + :param fragment_length: Estimated average fragment length. + :param sd: Estimated standard deviation of fragment length. + :param threads: Number of threads to use (default: 1). + :param genemap: File for mapping transcripts to genes. + :param gtf: GTF file for transcriptome information. + :param bootstrap_samples: Number of bootstrap samples (default: 0). + :param matrix_to_files: Reorganize matrix output into abundance tsv files. + :param matrix_to_directories: Reorganize matrix output into abundance tsv files across multiple directories. + :param seed: Seed for the bootstrap sampling (default: 42). + :param plaintext: Output plaintext only, not HDF5. + """ + if not index and not txnames: + return {"error": "Either index (-i) or txnames (-T) must be provided."} + + Path(output_dir).mkdir(parents=True, exist_ok=True) + + cmd = ["kallisto", "quant-tcc", "-o", output_dir, "-t", str(threads), "-b", str(bootstrap_samples), "--seed", str(seed)] + + if index: cmd.extend(["-i", index]) + if txnames: cmd.extend(["-T", txnames]) + if ec_file: cmd.extend(["-e", ec_file]) + if fragment_file: cmd.extend(["-f", fragment_file]) + if long_reads: cmd.append("--long") + if platform: cmd.extend(["-p", platform]) + if fragment_length is not None: cmd.extend(["-l", str(fragment_length)]) + if sd is not None: cmd.extend(["-s", str(sd)]) + if genemap: cmd.extend(["-g", genemap]) + if gtf: cmd.extend(["-G", gtf]) + if matrix_to_files: cmd.append("--matrix-to-files") + if matrix_to_directories: cmd.append("--matrix-to-directories") + if plaintext: cmd.append("--plaintext") + + try: + result = subprocess.run(cmd, capture_output=True, text=True, check=True) + return { + "command_executed": " ".join(cmd), + "stdout": result.stdout, + "stderr": result.stderr, + "output_directory": output_dir + } + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(cmd), + "error": str(e), + "stdout": e.stdout, + "stderr": e.stderr + } + +@mcp.tool() +def kallisto_h5dump( + h5_file: str, + output_dir: str +): + """ + Converts HDF5-formatted results (abundance.h5) to plaintext. + + :param h5_file: Path to the abundance.h5 file. + :param output_dir: Directory to write output to. + """ + if not Path(h5_file).exists(): + return {"error": f"HDF5 file not found: {h5_file}"} + + Path(output_dir).mkdir(parents=True, exist_ok=True) + + cmd = ["kallisto", "h5dump", "-o", output_dir, h5_file] + + try: + result = subprocess.run(cmd, capture_output=True, text=True, check=True) + return { + "command_executed": " ".join(cmd), + "stdout": result.stdout, + "stderr": result.stderr, + "output_directory": output_dir + } + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(cmd), + "error": str(e), + "stdout": e.stdout, + "stderr": e.stderr + } + +@mcp.tool() +def kallisto_inspect( + index_file: str, + threads: int = 1 +): + """ + Inspects and gives information about a kallisto index. + + :param index_file: Path to the kallisto index file. + :param threads: Number of threads to use. + """ + if not Path(index_file).exists(): + return {"error": f"Index file not found: {index_file}"} + + cmd = ["kallisto", "inspect", index_file, "-t", str(threads)] + + try: + result = subprocess.run(cmd, capture_output=True, text=True, check=True) + return { + "command_executed": " ".join(cmd), + "stdout": result.stdout, + "stderr": result.stderr + } + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(cmd), + "error": str(e), + "stdout": e.stdout, + "stderr": e.stderr + } + +@mcp.tool() +def kallisto_version(): + """Prints kallisto version information.""" + cmd = ["kallisto", "version"] + try: + result = subprocess.run(cmd, capture_output=True, text=True, check=True) + return {"version": result.stdout.strip()} + except subprocess.CalledProcessError as e: + return {"error": str(e), "stderr": e.stderr} + +@mcp.tool() +def kallisto_cite(): + """Prints citation information for kallisto.""" + cmd = ["kallisto", "cite"] + try: + result = subprocess.run(cmd, capture_output=True, text=True, check=True) + return {"citation": result.stdout.strip()} + except subprocess.CalledProcessError as e: + return {"error": str(e), "stderr": e.stderr} + +@mcp.tool() +def kallisto_bus_list_technologies(): + """Lists all single-cell technologies supported by kallisto bus.""" + cmd = ["kallisto", "bus", "-l"] + try: + result = subprocess.run(cmd, capture_output=True, text=True, check=True) + return {"supported_technologies": result.stdout} + except subprocess.CalledProcessError as e: + # kallisto bus -l might return exit code 0 or 1 depending on version + return {"supported_technologies": e.stdout if e.stdout else e.stderr} + +@mcp.tool() +def kallisto_merge( + output_dir: str, + input_dirs: List[str] +): + """ + Merges several batch runs. + + :param output_dir: Directory to write merged output to. + :param input_dirs: List of directories containing kallisto results to merge. + """ + Path(output_dir).mkdir(parents=True, exist_ok=True) + + cmd = ["kallisto", "merge", "-o", output_dir] + cmd.extend(input_dirs) + + try: + result = subprocess.run(cmd, capture_output=True, text=True, check=True) + return { + "command_executed": " ".join(cmd), + "stdout": result.stdout, + "stderr": result.stderr, + "output_directory": output_dir + } + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(cmd), + "error": str(e), + "stdout": e.stdout, + "stderr": e.stderr + } diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_kallisto/app/kallisto_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_kallisto/app/kallisto_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..b9626ad27988d6af4e3364d5cfcd07ab45708d7a --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_kallisto/app/kallisto_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_kallisto/app/kallisto_server.py') +SERVER_NAME = 'biosci_kallisto' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_kallisto/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_kallisto/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_kallisto/app/requirements.txt @@ -0,0 +1 @@ + diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_kallisto/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_kallisto/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..ae6743240ef416df1b2d63ae865bc94a2e88660a --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_kallisto/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-kallisto: + build: . + image: mcp-kallisto:latest + container_name: mcp-kallisto + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=kallisto + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_kallisto/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_kallisto/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..67ed8c709fefd02655e0912c4987f578b8b1a42b --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_kallisto/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - kallisto + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_kallisto/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_kallisto/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_kallisto/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_liana/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_liana/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_liana/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_libsequence/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_libsequence/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..2100e99912a4b8e8e2314275f3860a7dbeed4d3a --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_libsequence/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install libsequence via conda (e.g., from bioconda) +RUN conda install -c bioconda libsequence -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY app/libsequence_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/libsequence_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/libsequence_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_libsequence/app/libsequence_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_libsequence/app/libsequence_server.py new file mode 100644 index 0000000000000000000000000000000000000000..4b87f886a1919068ae077a86dc2bc9a6d9b6e5cf --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_libsequence/app/libsequence_server.py @@ -0,0 +1,43 @@ +import subprocess +from pathlib import Path +from typing import Optional, List, Dict, Any + +# NO NEED to import mcp as per instructions, assuming it's available in the execution environment. + +@mcp.tool() +def libsequence_library_info(): + """ + Provides information about the libsequence C++ class library. + + Based on the provided documentation, libsequence is a C++ class library + for evolutionary genetics and does not expose direct command-line executables + or subcommands that can be run via `subprocess.run`. + + This function serves to inform users about the nature of the tool as described + in its documentation, rather than executing a command-line program. + """ + tool_name = "libsequence" + summary = "A C++ class library for evolutionary genetics." + description = "A C++ class library for evolutionary genetics." + home_url = "http://molpopgen.github.io/libsequence/" + + # Since the provided documentation indicates libsequence is a C++ class library + # and does not describe any command-line executables, we cannot perform + # a `subprocess.run` operation. Instead, we return structured information + # explaining this fact. + info_message = ( + f"Tool: {tool_name}\n" + f"Summary: {summary}\n" + f"Description: {description}\n" + f"Home URL: {home_url}\n\n" + "The provided documentation indicates that libsequence is a C++ class library " + "and does not expose command-line functions or executables. Therefore, " + "no `subprocess.run` operation can be performed for this tool as a direct CLI." + ) + + return { + "command_executed": f"No direct command-line executable found for '{tool_name}' based on documentation.", + "stdout": info_message, + "stderr": "", + "output_files": [], + } \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_libsequence/app/libsequence_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_libsequence/app/libsequence_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..07840244818a30ff94f41d5b5bdde2b32061881f --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_libsequence/app/libsequence_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_libsequence/app/libsequence_server.py') +SERVER_NAME = 'biosci_libsequence' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_libsequence/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_libsequence/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_libsequence/app/requirements.txt @@ -0,0 +1 @@ + diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_libsequence/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_libsequence/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..158fcea9ce40c28e902d44a7b7a619e13de4d21d --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_libsequence/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-libsequence: + build: . + image: mcp-libsequence:latest + container_name: mcp-libsequence + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=libsequence + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_libsequence/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_libsequence/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..7800304e4bfd85c7a76a2528cadef6f263efa430 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_libsequence/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - libsequence + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_libsequence/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_libsequence/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_libsequence/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_maxquant/app/maxquant_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_maxquant/app/maxquant_server.py new file mode 100644 index 0000000000000000000000000000000000000000..931952348db226d4f0ac212edaa18a8e6f558a56 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_maxquant/app/maxquant_server.py @@ -0,0 +1,242 @@ +import subprocess +import sys +import os +import shutil +import xml.etree.ElementTree as ET +from pathlib import Path +from typing import List, Optional + +# MCP decorator is commented out as per instructions, +# but would be present in a real MCP environment. +# from mcp import mcp + +@mcp.tool() +def maxquant_run( + mqpar_xml_file: Path, + maxquant_cmd_path: Path, + num_threads: Optional[int] = None, +) -> dict: + """ + Runs the MaxQuant analysis pipeline using a specified mqpar.xml configuration file. + + This tool executes the main MaxQuant quantification and identification workflow. + It intelligently locates the output 'combined/txt' directory by parsing the + mqpar.xml file to provide accurate output file paths. + + Args: + mqpar_xml_file: Path to the configured MaxQuant parameter file (mqpar.xml). + maxquant_cmd_path: Path to the MaxQuant command-line executable (MaxQuantCmd.exe). + num_threads: Optional number of threads for processing. If not set, MaxQuant + will use the maximum available. + + Returns: + A dictionary containing the execution details, including the command, + stdout, stderr, and a list of discovered output files. + """ + # 1. Input Validation + if not maxquant_cmd_path.is_file(): + raise FileNotFoundError(f"MaxQuant executable not found at: {maxquant_cmd_path}") + if not mqpar_xml_file.is_file(): + raise FileNotFoundError(f"MaxQuant parameter file not found at: {mqpar_xml_file}") + if num_threads is not None and num_threads <= 0: + raise ValueError("num_threads must be a positive integer.") + + # 2. Command Construction + base_command = ["mono"] if sys.platform != "win32" else [] + command = base_command + [str(maxquant_cmd_path), str(mqpar_xml_file)] + if num_threads is not None: + command.append(str(num_threads)) + command_str = " ".join(command) + + # 3. Subprocess Execution + working_dir = mqpar_xml_file.parent + try: + result = subprocess.run( + command, + capture_output=True, + text=True, + check=True, + cwd=working_dir + ) + stdout = result.stdout + stderr = result.stderr + + # 4. Output File Discovery (parsing mqpar.xml for accuracy) + output_files = [] + output_dir = None + try: + tree = ET.parse(mqpar_xml_file) + root = tree.getroot() + fixed_folder_node = root.find('.//fixedCombinedFolder') + + if fixed_folder_node is not None and fixed_folder_node.text: + output_dir = Path(fixed_folder_node.text) + else: + first_raw_file_node = root.find('.//filePaths/string[1]') + if first_raw_file_node is not None and first_raw_file_node.text: + first_raw_file_path = Path(first_raw_file_node.text) + if not first_raw_file_path.is_absolute(): + first_raw_file_path = mqpar_xml_file.parent / first_raw_file_path + output_dir = first_raw_file_path.parent + except ET.ParseError: + stderr += "\n[MCP Tool Warning] Could not parse mqpar.xml to determine exact output folder. Searching relative to the parameter file." + output_dir = working_dir + + if output_dir: + combined_dir = output_dir / "combined" + if combined_dir.is_dir(): + txt_dir = combined_dir / "txt" + if txt_dir.is_dir(): + output_files = [str(p) for p in txt_dir.glob("*.*")] + + # 5. Structured Return + return { + "command_executed": command_str, + "stdout": stdout, + "stderr": stderr, + "output_files": sorted(output_files) + } + + except subprocess.CalledProcessError as e: + return { + "command_executed": command_str, + "stdout": e.stdout, + "stderr": e.stderr, + "error": f"MaxQuant execution failed with return code {e.returncode}", + "output_files": [] + } + +@mcp.tool() +def maxquant_create_mqpar( + output_xml_path: Path, + fasta_files: List[Path], + maxquant_cmd_path: Path, +) -> dict: + """ + Creates a template MaxQuant parameter file (mqpar.xml) from FASTA files. + + This tool is a convenient way to bootstrap a MaxQuant analysis by generating + a configuration file that can be further customized. + + Args: + output_xml_path: The full path where the new mqpar.xml file will be saved. + fasta_files: A list of paths to FASTA database files. + maxquant_cmd_path: Path to the MaxQuant command-line executable (MaxQuantCmd.exe). + + Returns: + A dictionary containing the execution details and the path to the created file. + """ + # 1. Input Validation + if not maxquant_cmd_path.is_file(): + raise FileNotFoundError(f"MaxQuant executable not found at: {maxquant_cmd_path}") + if not fasta_files: + raise ValueError("At least one FASTA file must be provided.") + for f_path in fasta_files: + if not f_path.is_file(): + raise FileNotFoundError(f"FASTA file not found: {f_path}") + + output_xml_path.parent.mkdir(parents=True, exist_ok=True) + + # 2. Command Construction + base_command = ["mono"] if sys.platform != "win32" else [] + command = base_command + [str(maxquant_cmd_path), "--create-mqpar", str(output_xml_path)] + command.extend([str(f) for f in fasta_files]) + command_str = " ".join(command) + + # 3. Subprocess Execution + try: + result = subprocess.run( + command, + capture_output=True, + text=True, + check=True + ) + + output_files = [str(output_xml_path)] if output_xml_path.is_file() else [] + + return { + "command_executed": command_str, + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": output_files + } + except subprocess.CalledProcessError as e: + return { + "command_executed": command_str, + "stdout": e.stdout, + "stderr": e.stderr, + "error": f"MaxQuant mqpar creation failed with return code {e.returncode}", + "output_files": [] + } + +@mcp.tool() +def maxquant_change_folder( + mqpar_xml_file: Path, + new_data_folder: Path, + maxquant_cmd_path: Path, + output_xml_path: Optional[Path] = None, +) -> dict: + """ + Updates all file paths in a mqpar.xml file to point to a new data folder. + + This is useful for making a MaxQuant project portable or reusing a configuration + template with a new dataset. The tool works on a copy to prevent accidental + modification of the original file. + + Args: + mqpar_xml_file: Path to the source MaxQuant parameter file (mqpar.xml). + new_data_folder: Path to the new root folder containing the raw data files. + maxquant_cmd_path: Path to the MaxQuant command-line executable (MaxQuantCmd.exe). + output_xml_path: Optional path for the modified mqpar.xml file. If not provided, + a new file with an '_updated' suffix is created. + + Returns: + A dictionary containing execution details and the path to the modified file. + """ + # 1. Input Validation + if not maxquant_cmd_path.is_file(): + raise FileNotFoundError(f"MaxQuant executable not found at: {maxquant_cmd_path}") + if not mqpar_xml_file.is_file(): + raise FileNotFoundError(f"Input mqpar.xml file not found at: {mqpar_xml_file}") + if not new_data_folder.is_dir(): + raise NotADirectoryError(f"The new data folder does not exist or is not a directory: {new_data_folder}") + + # Work on a copy to avoid modifying the original file in-place + if output_xml_path: + target_xml_file = output_xml_path + else: + target_xml_file = mqpar_xml_file.with_name(f"{mqpar_xml_file.stem}_updated.xml") + + target_xml_file.parent.mkdir(parents=True, exist_ok=True) + shutil.copy(mqpar_xml_file, target_xml_file) + + # 2. Command Construction + base_command = ["mono"] if sys.platform != "win32" else [] + command = base_command + [str(maxquant_cmd_path), "--change-folder", str(target_xml_file), str(new_data_folder)] + command_str = " ".join(command) + + # 3. Subprocess Execution + try: + result = subprocess.run( + command, + capture_output=True, + text=True, + check=True + ) + + return { + "command_executed": command_str, + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": [str(target_xml_file)] + } + except subprocess.CalledProcessError as e: + if target_xml_file.exists(): + os.remove(target_xml_file) + return { + "command_executed": command_str, + "stdout": e.stdout, + "stderr": e.stderr, + "error": f"MaxQuant change-folder failed with return code {e.returncode}", + "output_files": [] + } \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_medaka/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_medaka/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..5ae0731bf52a3b96391cd25de5fcb35fa0c875a0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_medaka/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install medaka via conda (e.g., from bioconda) +RUN conda install -c bioconda medaka -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY app/medaka_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/medaka_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/medaka_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_medaka/app/medaka_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_medaka/app/medaka_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..8d5d7d3182b8609e78d9c3f84f53ab4c43069fb8 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_medaka/app/medaka_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_medaka/app/medaka_server.py') +SERVER_NAME = 'biosci_medaka' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_medaka/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_medaka/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_medaka/app/requirements.txt @@ -0,0 +1 @@ + diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_medaka/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_medaka/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..075875f5a0824da689e2e6f12efaca712b5797c0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_medaka/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-medaka: + build: . + image: mcp-medaka:latest + container_name: mcp-medaka + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=medaka + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_medaka/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_medaka/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..5b574a717527c3bb00979054d53105ba89f166ae --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_medaka/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - medaka + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_medaka/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_medaka/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_medaka/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_minimap2/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_minimap2/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..793f6222d68748cc27ea2c54cd23ba34849f1636 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_minimap2/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install minimap2 via conda (e.g., from bioconda) +RUN conda install -c bioconda minimap2 -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY app/minimap2_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/minimap2_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/minimap2_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_minimap2/app/minimap2_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_minimap2/app/minimap2_server.py new file mode 100644 index 0000000000000000000000000000000000000000..f9d2abc58c31307b2649730a9d37d90a0b0a584e --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_minimap2/app/minimap2_server.py @@ -0,0 +1,249 @@ +import subprocess +from pathlib import Path +from typing import Optional, List, Union +import tempfile + +@mcp.tool() +def minimap2_index( + target_fa: str, + output_mmi: str, + preset: Optional[str] = None, + k: Optional[int] = None, + w: Optional[int] = None, + H: bool = False, + I: Optional[str] = None, +): + """ + Indexes target sequences for faster mapping with minimap2. + + Args: + target_fa: Path to the target FASTA file. + output_mmi: Path where the index (.mmi) will be saved. + preset: Preset (e.g., map-ont, lr:hq, map-hifi, sr, etc.). Applied before other options. + k: K-mer size (no larger than 28). + w: Minimizer window size. + H: Use homopolymer-compressed k-mer (preferable for PacBio). + I: Split index for every ~NUM input bases (e.g., 8G, 4M). + """ + target_path = Path(target_fa) + if not target_path.exists(): + return {"error": f"Target file {target_fa} not found."} + + cmd = ["minimap2"] + + if preset: + cmd.extend(["-x", preset]) + if k is not None: + if k > 28: + return {"error": "k-mer size (k) cannot be larger than 28."} + cmd.extend(["-k", str(k)]) + if w is not None: + cmd.extend(["-w", str(w)]) + if H: + cmd.append("-H") + if I: + cmd.extend(["-I", I]) + + cmd.extend(["-d", output_mmi, target_fa]) + + try: + result = subprocess.run(cmd, check=True, capture_output=True, text=True) + return { + "command_executed": " ".join(cmd), + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": [output_mmi] + } + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(cmd), + "error": str(e), + "stdout": e.stdout, + "stderr": e.stderr + } + +@mcp.tool() +def minimap2_align( + target: str, + queries: List[str], + output_file: Optional[str] = None, + preset: Optional[str] = None, + sam_format: bool = False, + threads: int = 3, + k: Optional[int] = None, + w: Optional[int] = None, + H: bool = False, + I: Optional[str] = None, + f: Optional[float] = None, + g: Optional[str] = None, + G: Optional[str] = None, + F: Optional[int] = None, + r: Optional[str] = None, + n: Optional[int] = None, + m: Optional[int] = None, + X: bool = False, + p: Optional[float] = None, + N: Optional[int] = None, + A: Optional[int] = None, + B: Optional[int] = None, + O: Optional[str] = None, + E: Optional[str] = None, + z: Optional[str] = None, + s: Optional[int] = None, + u: Optional[str] = None, + J: Optional[int] = None, + L: bool = False, + R: Optional[str] = None, + write_cigar_paf: bool = False, + cs_tag: Optional[str] = None, + ds_tag: bool = False, + MD_tag: bool = False, + eqx: bool = False, + soft_clip_supplementary: bool = False, + minibatch_size: Optional[str] = None, + junc_bed: Optional[str] = None, + junc_bonus: Optional[int] = None, + secondary: bool = True, +): + """ + Performs sequence alignment using minimap2. + + Args: + target: Path to target.fa or target.idx. + queries: List of paths to query sequence files (FASTA/FASTQ). + output_file: Path to output file. If not provided, results are returned in stdout. + preset: Preset (map-ont, lr:hq, map-hifi, map-pb, map-iclr, asm5, asm10, asm20, splice, splice:hq, splice:sr, sr, ava-pb, ava-ont). + sam_format: Output in SAM format (default is PAF). + threads: Number of threads to use. + k: K-mer size (no larger than 28). + w: Minimizer window size. + H: Use homopolymer-compressed k-mer. + I: Split index for every ~NUM input bases. + f: Filter out top FLOAT fraction of repetitive minimizers. + g: Stop chain elongation if there are no minimizers in INT-bp. + G: Max intron length (effective with splice mode). + F: Max fragment length (effective with -xsr or fragment mode). + r: Chaining/alignment bandwidth and long-join bandwidth (e.g., "500,20000"). + n: Minimal number of minimizers on a chain. + m: Minimal chaining score. + X: Skip self and dual mappings (for all-vs-all mode). + p: Min secondary-to-primary score ratio. + N: Retain at most INT secondary alignments. + A: Matching score. + B: Mismatch penalty. + O: Gap open penalty (e.g., "4,24"). + E: Gap extension penalty (e.g., "2,1"). + z: Z-drop score and inversion Z-drop score (e.g., "400,200"). + s: Minimal peak DP alignment score. + u: How to find GT-AG. f:transcript strand, b:both strands, n:don't match. + J: Splice mode. 0: original, 1: miniprot. + L: Write CIGAR with >65535 ops at the CG tag. + R: SAM read group line (e.g., '@RG\\tID:foo\\tSM:bar'). + write_cigar_paf: Output CIGAR in PAF (cg tag). + cs_tag: Output the cs tag ('short' or 'long'). + ds_tag: Output the ds tag (extension to cs). + MD_tag: Output the MD tag. + eqx: Write =/X CIGAR operators. + soft_clip_supplementary: Use soft clipping for supplementary alignments. + minibatch_size: Minibatch size for mapping (e.g., 500M). + junc_bed: Gene annotations in BED12 or intron positions in 5-column BED. + junc_bonus: Score bonus for a splice donor or acceptor found in annotation. + secondary: Whether to output secondary alignments. + """ + target_path = Path(target) + if not target_path.exists(): + return {"error": f"Target file {target} not found."} + + for q in queries: + if not Path(q).exists(): + return {"error": f"Query file {q} not found."} + + cmd = ["minimap2"] + + # Preset must be applied first + if preset: + cmd.extend(["-x", preset]) + + # Indexing/Mapping Options + if k is not None: + if k > 28: return {"error": "k-mer size (k) cannot be larger than 28."} + cmd.extend(["-k", str(k)]) + if w is not None: cmd.extend(["-w", str(w)]) + if H: cmd.append("-H") + if I: cmd.extend(["-I", I]) + if f is not None: cmd.extend(["-f", str(f)]) + if g: cmd.extend(["-g", g]) + if G: cmd.extend(["-G", G]) + if F is not None: cmd.extend(["-F", str(F)]) + if r: cmd.extend(["-r", r]) + if n is not None: cmd.extend(["-n", str(n)]) + if m is not None: cmd.extend(["-m", str(m)]) + if X: cmd.append("-X") + if p is not None: cmd.extend(["-p", str(p)]) + if N is not None: cmd.extend(["-N", str(N)]) + + # Alignment Options + if A is not None: cmd.extend(["-A", str(A)]) + if B is not None: cmd.extend(["-B", str(B)]) + if O: cmd.extend(["-O", O]) + if E: cmd.extend(["-E", E]) + if z: cmd.extend(["-z", z]) + if s is not None: cmd.extend(["-s", str(s)]) + if u: cmd.extend(["-u", u]) + if J is not None: cmd.extend(["-J", str(J)]) + if junc_bed: cmd.extend(["--junc-bed", junc_bed]) + if junc_bonus is not None: cmd.extend(["--junc-bonus", str(junc_bonus)]) + + # I/O Options + if sam_format: cmd.append("-a") + if output_file: cmd.extend(["-o", output_file]) + if L: cmd.append("-L") + if R: cmd.extend(["-R", R]) + if write_cigar_paf: cmd.append("-c") + if cs_tag: + cmd.append(f"--cs={cs_tag}" if cs_tag in ["short", "long"] else "--cs") + if ds_tag: cmd.append("--ds") + if MD_tag: cmd.append("--MD") + if eqx: cmd.append("--eqx") + if soft_clip_supplementary: cmd.append("-Y") + if minibatch_size: cmd.extend(["-K", minibatch_size]) + if not secondary: cmd.append("--secondary=no") + + cmd.extend(["-t", str(threads)]) + + # Positional arguments + cmd.append(target) + cmd.extend(queries) + + try: + result = subprocess.run(cmd, check=True, capture_output=True, text=True) + + output_info = { + "command_executed": " ".join(cmd), + "stdout": result.stdout if not output_file else f"Output written to {output_file}", + "stderr": result.stderr, + } + if output_file: + output_info["output_files"] = [output_file] + + return output_info + + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(cmd), + "error": str(e), + "stdout": e.stdout, + "stderr": e.stderr + } + +@mcp.tool() +def minimap2_get_version(): + """Returns the version of minimap2 installed.""" + try: + result = subprocess.run(["minimap2", "--version"], check=True, capture_output=True, text=True) + return { + "version": result.stdout.strip(), + "stderr": result.stderr + } + except subprocess.CalledProcessError as e: + return {"error": str(e), "stderr": e.stderr} \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_minimap2/app/minimap2_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_minimap2/app/minimap2_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..0ed3d19bf03af202ef35ed698c60b88b8eb816ba --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_minimap2/app/minimap2_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_minimap2/app/minimap2_server.py') +SERVER_NAME = 'biosci_minimap2' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_minimap2/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_minimap2/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_minimap2/app/requirements.txt @@ -0,0 +1 @@ + diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_minimap2/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_minimap2/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..cb3d2d3316d8323d63147ca1248b6fea882ad58b --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_minimap2/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-minimap2: + build: . + image: mcp-minimap2:latest + container_name: mcp-minimap2 + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=minimap2 + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_minimap2/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_minimap2/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..154d9e7c7a9e6be3f612a2d2bd8a20950f903714 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_minimap2/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - minimap2 + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_minimap2/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_minimap2/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_minimap2/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_mmtf-python/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_mmtf-python/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..9df5a1c75dce2b7201e3661272618f79297a5ab8 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_mmtf-python/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install mmtf-python via conda (e.g., from bioconda) +RUN conda install -c bioconda mmtf-python -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY app/mmtf-python_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/mmtf-python_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/mmtf-python_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_mmtf-python/app/mmtf-python_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_mmtf-python/app/mmtf-python_server.py new file mode 100644 index 0000000000000000000000000000000000000000..f2b9d7292e0ab3889971e440daae40d18fe20287 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_mmtf-python/app/mmtf-python_server.py @@ -0,0 +1,106 @@ +import subprocess +from pathlib import Path +from typing import List + +# MCP decorator placeholder +# In a real MCP environment, this would be: +# from mcp import mcp +# @mcp.tool() +def mcp_tool_decorator(): + def decorator(func): + return func + return decorator + +@mcp_tool_decorator() +def fetch_mmtf( + pdb_id: str, + output_file: Path, +): + """ + Fetches a PDB entry in MMTF format from the RCSB PDB and saves it to a file. + + This tool acts as a command-line wrapper for the 'mmtf-python' library, + which does not have its own executable. It uses the library's `fetch` and + `write_mmtf` functions to download and save the specified structure. + + Args: + pdb_id (str): The 4-character PDB ID to fetch (e.g., '1AQ1'). + output_file (Path): The path to save the output MMTF file. + """ + # --- Input Validation --- + if not pdb_id or len(pdb_id) != 4 or not pdb_id.isalnum(): + raise ValueError("pdb_id must be a 4-character alphanumeric string.") + + if not output_file.parent.exists(): + try: + output_file.parent.mkdir(parents=True, exist_ok=True) + except OSError as e: + raise OSError(f"Failed to create output directory {output_file.parent}: {e}") + + # --- Command-line script construction --- + # This Python one-liner imports the necessary functions from the mmtf library, + # fetches the data for the given PDB ID, and writes it to the specified output file. + py_command = ( + f"from mmtf import fetch, write_mmtf; " + f"mmtf_data = fetch('{pdb_id}'); " + f"write_mmtf('{str(output_file)}', mmtf_data)" + ) + + cmd = ["python", "-c", py_command] + command_executed = " ".join(cmd) + + # --- Subprocess Execution --- + try: + process = subprocess.run( + cmd, + capture_output=True, + text=True, + check=True, + encoding='utf-8' + ) + + if not output_file.exists() or output_file.stat().st_size == 0: + # This case handles situations where the command succeeds (exit code 0) + # but the output file is not created or is empty, which can happen + # if the PDB ID is valid but has no MMTF data available. + error_message = ( + f"Command executed successfully, but the output file was not created or is empty. " + f"This may indicate that MMTF data is not available for PDB ID '{pdb_id}'.\n" + f"Stderr: {process.stderr}" + ) + return { + "error": error_message, + "command_executed": command_executed, + "stdout": process.stdout, + "stderr": process.stderr, + "return_code": 0, + } + + return { + "command_executed": command_executed, + "stdout": process.stdout, + "stderr": process.stderr, + "output_files": [str(output_file)] + } + except FileNotFoundError: + # This error occurs if the 'python' executable is not found in the system's PATH. + return { + "error": "Python executable not found. Ensure Python is installed and in the system's PATH.", + "command_executed": command_executed, + } + except subprocess.CalledProcessError as e: + # This catches non-zero exit codes from the subprocess, which typically indicate + # an error within the mmtf-python library (e.g., PDB ID not found, network issues). + return { + "error": f"Execution failed with return code {e.returncode}.", + "command_executed": command_executed, + "stdout": e.stdout, + "stderr": e.stderr, + "return_code": e.returncode, + } + except Exception as e: + # Catch any other unexpected errors during execution. + return { + "error": f"An unexpected error occurred: {str(e)}", + "command_executed": command_executed, + } \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_mmtf-python/app/mmtf-python_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_mmtf-python/app/mmtf-python_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..c044feeedd4fcfd4e2da1f60100035d58850ad8f --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_mmtf-python/app/mmtf-python_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_mmtf-python/app/mmtf-python_server.py') +SERVER_NAME = 'biosci_mmtf_python' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_mmtf-python/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_mmtf-python/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_mmtf-python/app/requirements.txt @@ -0,0 +1 @@ + diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_mmtf-python/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_mmtf-python/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..828e949edea85fb49e8023afe6dc87600de6894f --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_mmtf-python/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-mmtf-python: + build: . + image: mcp-mmtf-python:latest + container_name: mcp-mmtf-python + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=mmtf-python + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_mmtf-python/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_mmtf-python/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..68067e493c4e1ab1dc546512161117070f6ca2b7 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_mmtf-python/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - mmtf-python + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_mmtf-python/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_mmtf-python/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_mmtf-python/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_moments/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_moments/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..295486df953e18f297f3a625e31029e7cdffb402 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_moments/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install moments via conda (e.g., from bioconda) +RUN conda install -c bioconda moments -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY app/moments_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/moments_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/moments_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_moments/app/moments_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_moments/app/moments_server.py new file mode 100644 index 0000000000000000000000000000000000000000..6e67f0d6d72202e88689fe0f9b76bbd013473eb6 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_moments/app/moments_server.py @@ -0,0 +1,445 @@ +import subprocess +import sys +from pathlib import Path +from typing import Optional, List, Union +import json +import tempfile + +@mcp.tool() +def moments_vcf_to_sfs( + vcf_path: str, + pop_info_path: str, + populations: List[str], + projections: List[int], + output_path: str, + polarized: bool = False, +): + """ + Parses a VCF file and generates a Site Frequency Spectrum (SFS). + + :param vcf_path: Path to the input VCF file. + :param pop_info_path: Path to the population information file (mapping individuals to populations). + :param populations: List of population IDs to include in the SFS. + :param projections: List of sample sizes (number of chromosomes) to project down to for each population. + :param output_path: Path to save the resulting SFS file (usually .fs). + :param polarized: Whether the SFS should be polarized (requires ancestral allele information in VCF). + """ + vcf_p = Path(vcf_path) + pop_p = Path(pop_info_path) + out_p = Path(output_path) + + if not vcf_p.exists(): + return {"error": f"VCF file not found: {vcf_path}"} + if not pop_p.exists(): + return {"error": f"Population info file not found: {pop_info_path}"} + if len(populations) != len(projections): + return {"error": "The number of populations must match the number of projections."} + + # Construct Python script to run moments + python_script = f""" +import moments +import sys + +try: + sfs = moments.Spectrum.from_vcf( + '{vcf_path}', + popfile='{pop_info_path}', + populations={populations}, + projections={projections}, + polarized={polarized} + ) + sfs.to_file('{output_path}') + print("Successfully created SFS.") +except Exception as e: + print(f"Error: {{str(e)}}", file=sys.stderr) + sys.exit(1) +""" + + try: + result = subprocess.run( + [sys.executable, "-c", python_script], + capture_output=True, + text=True, + check=True + ) + return { + "command_executed": f"moments.Spectrum.from_vcf('{vcf_path}', ...)", + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": [output_path] + } + except subprocess.CalledProcessError as e: + return { + "error": "Failed to parse VCF to SFS", + "stdout": e.stdout, + "stderr": e.stderr + } + +@mcp.tool() +def moments_vcf_to_ld_stats( + vcf_path: str, + pop_info_path: str, + populations: List[str], + output_path: str, + bins: Optional[List[float]] = None, + use_cache: bool = True, +): + """ + Computes Linkage Disequilibrium (LD) statistics from a VCF file. + + :param vcf_path: Path to the input VCF file. + :param pop_info_path: Path to the population information file. + :param populations: List of population IDs to include. + :param output_path: Path to save the computed LD statistics (usually .hdf5 or .p). + :param bins: Recombination distance bins (list of floats). If None, default bins are used. + :param use_cache: Whether to use caching for intermediate steps. + """ + vcf_p = Path(vcf_path) + pop_p = Path(pop_info_path) + + if not vcf_p.exists(): + return {"error": f"VCF file not found: {vcf_path}"} + if not pop_p.exists(): + return {"error": f"Population info file not found: {pop_info_path}"} + + bins_str = str(bins) if bins is not None else "None" + + python_script = f""" +import moments.LD +import sys + +try: + ld_stats = moments.LD.Parsing.compute_ld_statistics( + '{vcf_path}', + pop_file='{pop_info_path}', + pops={populations}, + bins={bins_str}, + use_cache={use_cache} + ) + # moments.LD statistics are typically saved using pickle or hdf5 + import pickle + with open('{output_path}', 'wb') as f: + pickle.dump(ld_stats, f) + print("Successfully computed LD statistics.") +except Exception as e: + print(f"Error: {{str(e)}}", file=sys.stderr) + sys.exit(1) +""" + + try: + result = subprocess.run( + [sys.executable, "-c", python_script], + capture_output=True, + text=True, + check=True + ) + return { + "command_executed": "moments.LD.Parsing.compute_ld_statistics(...)", + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": [output_path] + } + except subprocess.CalledProcessError as e: + return { + "error": "Failed to compute LD statistics", + "stdout": e.stdout, + "stderr": e.stderr + } + +@mcp.tool() +def moments_simulate_sfs_demes( + demes_path: str, + sample_sizes: List[int], + output_path: str, + sampled_demes: Optional[List[str]] = None, +): + """ + Simulates an SFS from a demographic model specified in a Demes file. + + :param demes_path: Path to the Demes YAML file. + :param sample_sizes: List of sample sizes for each population. + :param output_path: Path to save the simulated SFS. + :param sampled_demes: List of deme IDs to sample. If None, all leaf demes are sampled. + """ + if not Path(demes_path).exists(): + return {"error": f"Demes file not found: {demes_path}"} + + sampled_demes_str = str(sampled_demes) if sampled_demes is not None else "None" + + python_script = f""" +import moments +import sys + +try: + sfs = moments.Spectrum.from_demes( + '{demes_path}', + sampled_demes={sampled_demes_str}, + sample_sizes={sample_sizes} + ) + sfs.to_file('{output_path}') + print("Successfully simulated SFS from Demes.") +except Exception as e: + print(f"Error: {{str(e)}}", file=sys.stderr) + sys.exit(1) +""" + + try: + result = subprocess.run( + [sys.executable, "-c", python_script], + capture_output=True, + text=True, + check=True + ) + return { + "command_executed": "moments.Spectrum.from_demes(...)", + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": [output_path] + } + except subprocess.CalledProcessError as e: + return { + "error": "Failed to simulate SFS from Demes", + "stdout": e.stdout, + "stderr": e.stderr + } + +@mcp.tool() +def moments_infer_sfs( + sfs_path: str, + model_file: str, + model_func_name: str, + p0: List[float], + lower_bound: List[float], + upper_bound: List[float], + output_path: str, + maxiter: int = 100, + verbose: int = 1, +): + """ + Fits a demographic model to an observed SFS using log-likelihood optimization. + + :param sfs_path: Path to the observed SFS file. + :param model_file: Path to a Python script containing the demographic model function. + :param model_func_name: Name of the function in model_file that returns a simulated SFS. + :param p0: Initial parameter guesses. + :param lower_bound: Lower bounds for parameters. + :param upper_bound: Upper bounds for parameters. + :param output_path: Path to save the optimized parameters and results (JSON). + :param maxiter: Maximum number of optimization iterations. + :param verbose: Verbosity level for optimization. + """ + if not Path(sfs_path).exists(): + return {"error": f"SFS file not found: {sfs_path}"} + if not Path(model_file).exists(): + return {"error": f"Model file not found: {model_file}"} + + python_script = f""" +import moments +import sys +import json +import importlib.util + +# Load the user-defined model +spec = importlib.util.spec_from_file_location("user_model", "{model_file}") +user_model = importlib.util.module_from_spec(spec) +spec.loader.exec_module(user_model) +model_func = getattr(user_model, "{model_func_name}") + +try: + data = moments.Spectrum.from_file('{sfs_path}') + ns = data.sample_sizes + + # Wrap model to ensure it uses correct sample sizes + def func_to_optimize(params): + return model_func(params, ns) + + popt = moments.Inference.optimize_log( + {p0}, data, func_to_optimize, + lower_bound={lower_bound}, + upper_bound={upper_bound}, + maxiter={maxiter}, + verbose={verbose} + ) + + model_sfs = func_to_optimize(popt) + ll = moments.Inference.ll_multinom(model_sfs, data) + + results = {{ + "optimized_params": popt.tolist(), + "max_log_likelihood": float(ll), + "model_func": "{model_func_name}" + }} + + with open('{output_path}', 'w') as f: + json.dump(results, f, indent=4) + print("Successfully completed SFS inference.") +except Exception as e: + print(f"Error: {{str(e)}}", file=sys.stderr) + sys.exit(1) +""" + + try: + result = subprocess.run( + [sys.executable, "-c", python_script], + capture_output=True, + text=True, + check=True + ) + return { + "command_executed": "moments.Inference.optimize_log(...)", + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": [output_path] + } + except subprocess.CalledProcessError as e: + return { + "error": "Failed SFS inference", + "stdout": e.stdout, + "stderr": e.stderr + } + +@mcp.tool() +def moments_simulate_two_locus( + sample_size: int, + theta: float, + rho: float, + output_path: str, + gamma: float = 0.0, + sel_type: str = "additive", +): + """ + Simulates a two-locus frequency spectrum. + + :param sample_size: Number of chromosomes to sample. + :param theta: Population-scaled mutation rate (4Nu). + :param rho: Population-scaled recombination rate (4Nr). + :param output_path: Path to save the two-locus spectrum. + :param gamma: Population-scaled selection coefficient (2Ns). + :param sel_type: Type of selection ('additive', 'dominant', etc.). + """ + if sample_size <= 0: + return {"error": "Sample size must be positive."} + if theta < 0 or rho < 0: + return {"error": "Theta and Rho must be non-negative."} + + python_script = f""" +import moments.TwoLocus +import sys + +try: + # Simulate steady state two-locus spectrum + tls = moments.TwoLocus.TLS.steady_state( + {sample_size}, theta={theta}, rho={rho}, gamma={gamma}, sel_type='{sel_type}' + ) + tls.to_file('{output_path}') + print("Successfully simulated Two-Locus spectrum.") +except Exception as e: + print(f"Error: {{str(e)}}", file=sys.stderr) + sys.exit(1) +""" + + try: + result = subprocess.run( + [sys.executable, "-c", python_script], + capture_output=True, + text=True, + check=True + ) + return { + "command_executed": "moments.TwoLocus.TLS.steady_state(...)", + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": [output_path] + } + except subprocess.CalledProcessError as e: + return { + "error": "Failed to simulate Two-Locus spectrum", + "stdout": e.stdout, + "stderr": e.stderr + } + +@mcp.tool() +def moments_ld_infer_demography( + ld_stats_path: str, + model_file: str, + model_func_name: str, + p0: List[float], + lower_bound: List[float], + upper_bound: List[float], + output_path: str, + maxiter: int = 100, +): + """ + Fits a demographic model to LD statistics. + + :param ld_stats_path: Path to the computed LD statistics (pickle file). + :param model_file: Path to a Python script containing the LD demographic model. + :param model_func_name: Name of the function in model_file. + :param p0: Initial parameter guesses. + :param lower_bound: Lower bounds for parameters. + :param upper_bound: Upper bounds for parameters. + :param output_path: Path to save the optimized parameters (JSON). + :param maxiter: Maximum number of optimization iterations. + """ + if not Path(ld_stats_path).exists(): + return {"error": f"LD stats file not found: {ld_stats_path}"} + if not Path(model_file).exists(): + return {"error": f"Model file not found: {model_file}"} + + python_script = f""" +import moments.LD +import sys +import json +import pickle +import importlib.util + +# Load the user-defined model +spec = importlib.util.spec_from_file_location("user_model", "{model_file}") +user_model = importlib.util.module_from_spec(spec) +spec.loader.exec_module(user_model) +model_func = getattr(user_model, "{model_func_name}") + +try: + with open('{ld_stats_path}', 'rb') as f: + data = pickle.load(f) + + popt = moments.LD.Inference.optimize_log( + {p0}, data, model_func, + lower_bound={lower_bound}, + upper_bound={upper_bound}, + maxiter={maxiter}, + verbose=True + ) + + results = {{ + "optimized_params": popt.tolist(), + "model_func": "{model_func_name}" + }} + + with open('{output_path}', 'w') as f: + json.dump(results, f, indent=4) + print("Successfully completed LD inference.") +except Exception as e: + print(f"Error: {{str(e)}}", file=sys.stderr) + sys.exit(1) +""" + + try: + result = subprocess.run( + [sys.executable, "-c", python_script], + capture_output=True, + text=True, + check=True + ) + return { + "command_executed": "moments.LD.Inference.optimize_log(...)", + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": [output_path] + } + except subprocess.CalledProcessError as e: + return { + "error": "Failed LD inference", + "stdout": e.stdout, + "stderr": e.stderr + } \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_moments/app/moments_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_moments/app/moments_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..f4a225d42457ec4ab1f5f60eabcd80c957e41cae --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_moments/app/moments_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_moments/app/moments_server.py') +SERVER_NAME = 'biosci_moments' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_moments/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_moments/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_moments/app/requirements.txt @@ -0,0 +1 @@ + diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_moments/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_moments/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..8af11c0bc2cd75533e71d0c9cfeb80ad2723f487 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_moments/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-moments: + build: . + image: mcp-moments:latest + container_name: mcp-moments + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=moments + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_moments/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_moments/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..afd8da0d9edc9fe93147b1def3e4eac92d096b6d --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_moments/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - moments + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_moments/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_moments/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_moments/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_mothur/app/mothur_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_mothur/app/mothur_server.py new file mode 100644 index 0000000000000000000000000000000000000000..2db070314c116be116ba06b6882e82a7e80cadc7 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_mothur/app/mothur_server.py @@ -0,0 +1,303 @@ +import subprocess +import tempfile +import logging +from pathlib import Path +from typing import List, Optional, Dict, Any + +# Configure logging +logging.basicConfig(level=logging.INFO) + +def _build_mothur_command_string(command_name: str, params: Dict[str, Any]) -> str: + """Builds a mothur-compatible command string from a dictionary of parameters.""" + param_list = [] + for key, value in params.items(): + if value is None: + continue + if isinstance(value, bool): + param_list.append(f"{key}={str(value).lower()}") + elif isinstance(value, Path): + # Use absolute paths to ensure mothur can find the files + param_list.append(f"{key}={str(value.resolve())}") + else: + param_list.append(f"{key}={value}") + return f"{command_name}({','.join(param_list)})" + +def _run_mothur(command_str: str) -> Dict[str, Any]: + """ + Executes a mothur command string in an isolated temporary directory. + + Args: + command_str: The mothur command to execute (e.g., "unique.seqs(fasta=...)"). + + Returns: + A dictionary containing execution details and output file paths. + """ + with tempfile.TemporaryDirectory() as temp_dir_str: + temp_dir = Path(temp_dir_str) + + # Prepend set.dir to control output location reliably + full_command_str = f"set.dir(output={temp_dir.resolve()});{command_str}" + + # The '#command' syntax is mothur's way of running a batch command + cmd = ["mothur", f"#{full_command_str}"] + command_executed = " ".join(cmd) + + try: + result = subprocess.run( + cmd, + capture_output=True, + text=True, + check=True, + cwd=temp_dir, # Run from temp_dir for safety + ) + + # Discover all files created in the output directory + output_files = [str(p.resolve()) for p in temp_dir.glob("*") if p.is_file()] + + return { + "command_executed": command_executed, + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": output_files, + } + except FileNotFoundError: + logging.error("mothur executable not found. Please ensure it is in your PATH.") + raise + except subprocess.CalledProcessError as e: + logging.error(f"Mothur command failed with exit code {e.returncode}") + logging.error(f"STDOUT: {e.stdout}") + logging.error(f"STDERR: {e.stderr}") + return { + "command_executed": command_executed, + "stdout": e.stdout, + "stderr": e.stderr, + "error": "Mothur command failed.", + "return_code": e.returncode, + "output_files": [] + } + +@mcp.tool() +def make_contigs( + ffastq: Optional[Path] = None, + rfastq: Optional[Path] = None, + file: Optional[Path] = None, + fname: Optional[Path] = None, + oligos: Optional[Path] = None, + processors: int = 1, + bdiffs: int = 0, + pdiffs: int = 0, + checkorient: bool = True, + insert: int = 25, + trimoverlap: bool = False, + deltaq: int = 6, + maxee: float = 1.0, +) -> Dict[str, Any]: + """ + Assembles paired-end DNA sequences into contigs. + + This command aligns and merges paired-end reads from FASTQ files to create longer, + contiguous sequences for further analysis. + """ + # Input validation + if not ((ffastq and rfastq) or file or fname): + raise ValueError("You must provide one of the following: 'file', 'fname', or both 'ffastq' and 'rfastq'.") + if (ffastq and not rfastq) or (rfastq and not ffastq): + raise ValueError("'ffastq' and 'rfastq' must be provided together.") + + if ffastq and not ffastq.is_file(): raise FileNotFoundError(f"File not found: {ffastq}") + if rfastq and not rfastq.is_file(): raise FileNotFoundError(f"File not found: {rfastq}") + if file and not file.is_file(): raise FileNotFoundError(f"File not found: {file}") + if fname and not fname.is_file(): raise FileNotFoundError(f"File not found: {fname}") + if oligos and not oligos.is_file(): raise FileNotFoundError(f"File not found: {oligos}") + + params = { + "ffastq": ffastq, + "rfastq": rfastq, + "file": file, + "fname": fname, + "oligos": oligos, + "processors": processors, + "bdiffs": bdiffs, + "pdiffs": pdiffs, + "checkorient": checkorient, + "insert": insert, + "trimoverlap": trimoverlap, + "deltaq": deltaq, + "maxee": maxee, + } + + command_str = _build_mothur_command_string("make.contigs", params) + return _run_mothur(command_str) + +@mcp.tool() +def screen_seqs( + fasta: Path, + count: Optional[Path] = None, + group: Optional[Path] = None, + taxonomy: Optional[Path] = None, + qfile: Optional[Path] = None, + contigsreport: Optional[Path] = None, + summary: Optional[Path] = None, + start: Optional[int] = None, + end: Optional[int] = None, + maxambig: int = 0, + maxhomop: int = 8, + minlength: Optional[int] = None, + maxlength: Optional[int] = None, + processors: int = 1, +) -> Dict[str, Any]: + """ + Removes sequences that do not meet specified quality criteria. + + This command filters sequences based on length, number of ambiguous bases, + and homopolymer length to improve the quality of the dataset. + """ + # Input validation + if not fasta.is_file(): raise FileNotFoundError(f"FASTA file not found: {fasta}") + if count and not count.is_file(): raise FileNotFoundError(f"Count file not found: {count}") + if group and not group.is_file(): raise FileNotFoundError(f"Group file not found: {group}") + if taxonomy and not taxonomy.is_file(): raise FileNotFoundError(f"Taxonomy file not found: {taxonomy}") + if qfile and not qfile.is_file(): raise FileNotFoundError(f"Quality file not found: {qfile}") + if contigsreport and not contigsreport.is_file(): raise FileNotFoundError(f"Contigs report file not found: {contigsreport}") + if summary and not summary.is_file(): raise FileNotFoundError(f"Summary file not found: {summary}") + + params = { + "fasta": fasta, + "count": count, + "group": group, + "taxonomy": taxonomy, + "qfile": qfile, + "contigsreport": contigsreport, + "summary": summary, + "start": start, + "end": end, + "maxambig": maxambig, + "maxhomop": maxhomop, + "minlength": minlength, + "maxlength": maxlength, + "processors": processors, + } + + command_str = _build_mothur_command_string("screen.seqs", params) + return _run_mothur(command_str) + +@mcp.tool() +def unique_seqs( + fasta: Optional[Path] = None, + count: Optional[Path] = None, + format: str = "name", +) -> Dict[str, Any]: + """ + Finds the unique sequences in a dataset and generates a count/name file. + + This command dereplicates a FASTA file, keeping only the unique sequences and + creating a corresponding file that tracks the abundance of each unique sequence. + """ + # Input validation + if not fasta and not count: + raise ValueError("Either 'fasta' or 'count' file must be provided.") + if fasta and count: + raise ValueError("Provide either 'fasta' or 'count', not both.") + if fasta and not fasta.is_file(): + raise FileNotFoundError(f"FASTA file not found: {fasta}") + if count and not count.is_file(): + raise FileNotFoundError(f"Count file not found: {count}") + + valid_formats = ["name", "count"] + if format not in valid_formats: + raise ValueError(f"Invalid format '{format}'. Must be one of {valid_formats}.") + + params = { + "fasta": fasta, + "count": count, + "format": format, + } + + command_str = _build_mothur_command_string("unique.seqs", params) + return _run_mothur(command_str) + +@mcp.tool() +def dist_seqs( + fasta: Path, + cutoff: float = 0.25, + output: str = "lt", + processors: int = 1, + calc: str = "onegap", + countends: bool = True, +) -> Dict[str, Any]: + """ + Calculates pairwise distances between DNA sequences. + + This command generates a distance matrix from an aligned FASTA file, which is a + prerequisite for clustering sequences into OTUs. + """ + # Input validation + if not fasta.is_file(): + raise FileNotFoundError(f"FASTA file not found: {fasta}") + + valid_outputs = ["lt", "square", "column"] + if output not in valid_outputs: + raise ValueError(f"Invalid output format '{output}'. Must be one of {valid_outputs}.") + + valid_calcs = ["onegap", "nogap"] + if calc not in valid_calcs: + raise ValueError(f"Invalid calc method '{calc}'. Must be one of {valid_calcs}.") + + params = { + "fasta": fasta, + "cutoff": cutoff, + "output": output, + "processors": processors, + "calc": calc, + "countends": countends, + } + + command_str = _build_mothur_command_string("dist.seqs", params) + return _run_mothur(command_str) + +@mcp.tool() +def cluster( + phylip: Optional[Path] = None, + column: Optional[Path] = None, + name: Optional[Path] = None, + count: Optional[Path] = None, + method: str = "furthest", + cutoff: Optional[float] = None, + precision: int = 100, +) -> Dict[str, Any]: + """ + Assigns sequences to OTUs based on a distance matrix. + + This command uses one of several clustering algorithms (furthest neighbor, + nearest neighbor, or average neighbor) to group sequences into OTUs. + """ + # Input validation + if not phylip and not column: + raise ValueError("Either a 'phylip' or 'column' formatted distance file must be provided.") + if phylip and column: + raise ValueError("Provide either 'phylip' or 'column', not both.") + if phylip and not phylip.is_file(): + raise FileNotFoundError(f"Phylip file not found: {phylip}") + if column and not column.is_file(): + raise FileNotFoundError(f"Column file not found: {column}") + if name and not name.is_file(): + raise FileNotFoundError(f"Name file not found: {name}") + if count and not count.is_file(): + raise FileNotFoundError(f"Count file not found: {count}") + + valid_methods = ["furthest", "nearest", "average"] + if method not in valid_methods: + raise ValueError(f"Invalid method '{method}'. Must be one of {valid_methods}.") + + params = { + "phylip": phylip, + "column": column, + "name": name, + "count": count, + "method": method, + "cutoff": cutoff, + "precision": precision, + } + + command_str = _build_mothur_command_string("cluster", params) + return _run_mothur(command_str) \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_mothur/app/mothur_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_mothur/app/mothur_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..c35f439c016271418eade938b6f637669d50a3c7 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_mothur/app/mothur_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_mothur/app/mothur_server.py') +SERVER_NAME = 'biosci_mothur' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_mothur/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_mothur/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..39de0565c285d4fc82e19e4a7ba2fe1edc711c84 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_mothur/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - mothur + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_msproteomicstools/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_msproteomicstools/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..f5adb3268175b55dfd17b8cee7fd37afdf6f02ce --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_msproteomicstools/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install msproteomicstools via conda (e.g., from bioconda) +RUN conda install -c bioconda msproteomicstools -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY app/msproteomicstools_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/msproteomicstools_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/msproteomicstools_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_msproteomicstools/app/msproteomicstools_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_msproteomicstools/app/msproteomicstools_server.py new file mode 100644 index 0000000000000000000000000000000000000000..a3e231d19f10d20757924fd7068e890cdaaad883 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_msproteomicstools/app/msproteomicstools_server.py @@ -0,0 +1,117 @@ +import subprocess +import tempfile +from pathlib import Path +from typing import List, Optional + +# Note: As the provided documentation does not detail the specific command-line +# interface for the executables within msproteomicstools, this MCP tool is a +# hypothetical implementation for the 'TRIC' alignment tool. The parameters +# are based on common patterns for such tools. The actual executable name and +# arguments may differ. + +# Assuming the main command-line tool is an executable named 'tric' +# which would be available in the environment's PATH. + +@mcp.tool() +def tric_alignment( + input_files: List[Path], + output_file: Path, + method: str = "global", + max_rt_diff: float = 30.0, + target_fdr: float = 0.01, + use_rt_score: bool = False, + force_precursors: bool = False, +) -> dict: + """ + Aligns multiple targeted proteomics runs using the TRIC (TRansfer of Identification Confidence) algorithm. + + This tool takes multiple input files (e.g., from an OpenSWATH workflow), aligns them based on + retention time, and produces a single, aligned feature matrix. + + Args: + input_files: A list of input feature files (e.g., .tsv from OpenSWATH) to be aligned. + output_file: The path to the output file where the aligned matrix will be saved. + method: The alignment method to be used. Common options might include 'global' or 'local'. + max_rt_diff: Maximum retention time difference for features to be considered for alignment (in seconds). + target_fdr: The target False Discovery Rate (FDR) for scoring. + use_rt_score: Whether to use the retention time score for alignment. + force_precursors: Force the use of precursors for alignment, even if fragment ions are available. + + Returns: + A dictionary containing the execution command, stdout, stderr, and the path to the output file. + """ + # 1. Input Validation + if not input_files: + raise ValueError("At least one input file must be provided.") + for f in input_files: + if not f.is_file(): + raise FileNotFoundError(f"Input file not found: {f}") + + if not output_file.parent.exists(): + raise FileNotFoundError(f"Output directory does not exist: {output_file.parent}") + + if method not in ["global", "local"]: + raise ValueError(f"Invalid alignment method '{method}'. Must be 'global' or 'local'.") + + if max_rt_diff <= 0: + raise ValueError("max_rt_diff must be a positive value.") + + if not (0 < target_fdr < 1): + raise ValueError("target_fdr must be between 0 and 1.") + + # 2. Command Construction + # The actual command and arguments are hypothetical due to lack of specific documentation. + # We assume a syntax like: `tric --in file1.tsv file2.tsv --out aligned.tsv --method global ...` + cmd = [ + "tric", + "--in", + *[str(f) for f in input_files], + "--out", + str(output_file), + "--method", + method, + "--max_rt_diff", + str(max_rt_diff), + "--target_fdr", + str(target_fdr), + ] + + if use_rt_score: + cmd.append("--use_rt_score") + if force_precursors: + cmd.append("--force_precursors") + + command_executed = " ".join(cmd) + + # 3. Subprocess Execution + try: + result = subprocess.run( + cmd, + capture_output=True, + text=True, + check=True, + ) + stdout = result.stdout + stderr = result.stderr + except FileNotFoundError: + return { + "command_executed": command_executed, + "stdout": "", + "stderr": "Error: 'tric' executable not found. Please ensure msproteomicstools is installed and in your PATH.", + "output_files": [], + } + except subprocess.CalledProcessError as e: + return { + "command_executed": command_executed, + "stdout": e.stdout, + "stderr": e.stderr, + "output_files": [], + } + + # 4. Structured Result Return + return { + "command_executed": command_executed, + "stdout": stdout, + "stderr": stderr, + "output_files": [str(output_file)] + } diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_msproteomicstools/app/msproteomicstools_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_msproteomicstools/app/msproteomicstools_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..bf19e5c4ae8c2497600f452832e47637efb6b2da --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_msproteomicstools/app/msproteomicstools_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_msproteomicstools/app/msproteomicstools_server.py') +SERVER_NAME = 'biosci_msproteomicstools' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_msproteomicstools/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_msproteomicstools/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_msproteomicstools/app/requirements.txt @@ -0,0 +1 @@ + diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_msproteomicstools/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_msproteomicstools/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..3a1a0b54218ce6791de7cb248432b44066fccd13 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_msproteomicstools/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-msproteomicstools: + build: . + image: mcp-msproteomicstools:latest + container_name: mcp-msproteomicstools + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=msproteomicstools + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_msproteomicstools/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_msproteomicstools/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..7848ab7e9e2e9f3952881f2698e5714f8675bdd6 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_msproteomicstools/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - msproteomicstools + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_msproteomicstools/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_msproteomicstools/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_msproteomicstools/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_mummer/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_mummer/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..3b19513aadf0570e3c4efa714ff33977f398971d --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_mummer/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install mummer via conda (e.g., from bioconda) +RUN conda install -c bioconda mummer -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY app/mummer_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/mummer_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/mummer_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_mummer/app/mummer_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_mummer/app/mummer_server.py new file mode 100644 index 0000000000000000000000000000000000000000..732e81e95500eafb1207f63c9324d3a151604764 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_mummer/app/mummer_server.py @@ -0,0 +1,137 @@ +import subprocess +import tempfile +import shutil +from pathlib import Path +from typing import List, Optional, Dict, Any + +@mcp.tool() +def mummer_align( + reference_fasta: Path, + query_fasta: Path, + min_match_length: Optional[int] = None, + report_unique_matches: bool = False, + break_ties: bool = False, + report_reverse_complement: bool = False, + forward_strand_only: bool = False, + diagonal_output: bool = False, + output_prefix: Optional[str] = None, +) -> Dict[str, Any]: + """ + Aligns two nucleotide sequences using the 'mummer' executable to find maximal exact matches. + + This tool finds maximal exact matches between a reference and a query sequence. + It is part of the MUMmer suite and is typically used for aligning closely related sequences. + Note: The 'mummer' executable itself does not support a '--help' flag, so parameters are + inferred from common usage and MUMmer 3.x documentation. + + Args: + reference_fasta: Path to the reference FASTA file. + query_fasta: Path to the query FASTA file. + min_match_length: Minimum length of a match to report. If None, uses the tool's internal default + (typically 20 for mummer). Must be a positive integer. + report_unique_matches: If True, report only maximal unique matches (-mum flag). + break_ties: If True, break ties by reporting only one match per query position (-b flag). + report_reverse_complement: If True, report matches on the reverse complement strand of the query (-c flag). + forward_strand_only: If True, only report matches on the forward strand of the query (-F flag). + Cannot be used simultaneously with `report_reverse_complement`. + diagonal_output: If True, output matches in a diagonal format suitable for mummerplot (-D flag). + output_prefix: Prefix for output files. If provided, mummer will write output to files + like `.mums` in a temporary directory. + If None, output will be directed to stdout. + + Returns: + A dictionary containing: + - command_executed: The command string executed. + - stdout: Standard output from the mummer command. + - stderr: Standard error from the mummer command. + - output_files: A list of paths to any generated output files. + - error: An error message if the command fails. + """ + # Input validation + if not isinstance(reference_fasta, Path) or not reference_fasta.is_file(): + raise FileNotFoundError(f"Reference FASTA file not found or is not a valid path: {reference_fasta}") + if not isinstance(query_fasta, Path) or not query_fasta.is_file(): + raise FileNotFoundError(f"Query FASTA file not found or is not a valid path: {query_fasta}") + + if min_match_length is not None: + if not isinstance(min_match_length, int) or min_match_length <= 0: + raise ValueError("Minimum match length must be a positive integer.") + + if forward_strand_only and report_reverse_complement: + raise ValueError("Cannot use both 'forward_strand_only' and 'report_reverse_complement' simultaneously.") + + command: List[str] = ["mummer"] + + if report_unique_matches: + command.append("-mum") + if min_match_length is not None: + command.extend(["-l", str(min_match_length)]) + if break_ties: + command.append("-b") + if report_reverse_complement: + command.append("-c") + if forward_strand_only: + command.append("-F") + if diagonal_output: + command.append("-D") + + temp_dir: Optional[Path] = None + output_files: List[Path] = [] + stdout_content: str = "" + stderr_content: str = "" + + try: + if output_prefix: + # Create a temporary directory to run mummer and collect its output files + temp_dir = Path(tempfile.mkdtemp()) + # mummer writes files with the prefix in the current working directory. + # So, we change the working directory for the subprocess. + command.extend(["-p", output_prefix]) + + command.extend([str(reference_fasta), str(query_fasta)]) + + process = subprocess.run( + command, + cwd=temp_dir if temp_dir else None, # Run in temp_dir if output_prefix is used + capture_output=True, + text=True, + check=True + ) + stdout_content = process.stdout + stderr_content = process.stderr + + if temp_dir: + # Collect all files generated in the temporary directory + for f in temp_dir.iterdir(): + output_files.append(f) + + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(command), + "stdout": e.stdout, + "stderr": e.stderr, + "output_files": [], + "error": f"Mummer command failed with exit code {e.returncode}", + } + except FileNotFoundError: + return { + "command_executed": " ".join(command), + "stdout": "", + "stderr": "mummer executable not found. Please ensure it is installed and in your PATH.", + "output_files": [], + "error": "Mummer executable not found.", + } + finally: + # Clean up the temporary directory only if it was created and no output files + # were successfully collected (e.g., due to an error). + # If output_files are collected, the MCP framework is responsible for + # moving them out and then cleaning up the temporary directory. + if temp_dir and not output_files: + shutil.rmtree(temp_dir) + + return { + "command_executed": " ".join(command), + "stdout": stdout_content, + "stderr": stderr_content, + "output_files": output_files, + } \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_mummer/app/mummer_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_mummer/app/mummer_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..34f862e09c004b62dbf722d843735bc501a4b8ca --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_mummer/app/mummer_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_mummer/app/mummer_server.py') +SERVER_NAME = 'biosci_mummer' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_mummer/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_mummer/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_mummer/app/requirements.txt @@ -0,0 +1 @@ + diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_mummer/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_mummer/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..5232fc6c2c20708fc86a9ae5fbf6dce01bb61da8 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_mummer/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-mummer: + build: . + image: mcp-mummer:latest + container_name: mcp-mummer + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=mummer + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_mummer/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_mummer/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..2bc292155040a117de025edde34a34f505605e40 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_mummer/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - mummer + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_mummer/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_mummer/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_mummer/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_muscle/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_muscle/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..c844af59cd0f28ad26f6f5017124f0303ed27fc4 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_muscle/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install muscle via conda (e.g., from bioconda) +RUN conda install -c bioconda muscle -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY app/muscle_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/muscle_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/muscle_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_muscle/app/muscle_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_muscle/app/muscle_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..0f4cfa06f32437b38c3971f1d1035cdd68fab360 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_muscle/app/muscle_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_muscle/app/muscle_server.py') +SERVER_NAME = 'biosci_muscle' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_muscle/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_muscle/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_muscle/app/requirements.txt @@ -0,0 +1 @@ + diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_muscle/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_muscle/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..bb278bb750e1fbff5805fbc017e37e1636cf62f3 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_muscle/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-muscle: + build: . + image: mcp-muscle:latest + container_name: mcp-muscle + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=muscle + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_muscle/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_muscle/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..0ab8015973a700170e27d49efc7c49a0276a3da2 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_muscle/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - muscle + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_muscle/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_muscle/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_muscle/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_ncbi-vdb/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_ncbi-vdb/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..479fdf7aa267fe3a9d71aa278f8ed3929cada861 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_ncbi-vdb/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install ncbi-vdb via conda (e.g., from bioconda) +RUN conda install -c bioconda ncbi-vdb -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY app/ncbi-vdb_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/ncbi-vdb_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/ncbi-vdb_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_ncbi-vdb/app/ncbi-vdb_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_ncbi-vdb/app/ncbi-vdb_server.py new file mode 100644 index 0000000000000000000000000000000000000000..ea56bedef1fd9ba7fae221cb939d24f436cd49ac --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_ncbi-vdb/app/ncbi-vdb_server.py @@ -0,0 +1,84 @@ +import subprocess +from pathlib import Path +from typing import Optional, List, Dict, Any +import logging + +# Set up logging +logging.basicConfig(level=logging.INFO) +log = logging.getLogger(__name__) + +class MCP: + """ + A dummy class to mock the @mcp.tool decorator for standalone execution. + In a real MCP environment, this would be provided by the MCP framework. + """ + def tool(self): + def decorator(func): + def wrapper(*args, **kwargs): + return func(*args, **kwargs) + return wrapper + return decorator + +mcp = MCP() + +@mcp.tool() +def run_ncbi_vdb(args: Optional[str] = None) -> Dict[str, Any]: + """ + Executes the ncbi-vdb command with arbitrary arguments. + + NOTE: The provided documentation for `ncbi-vdb` does not specify any + command-line subcommands or parameters. It is described as a database + engine and library for the SRA toolkit. This function serves as a generic + wrapper for a hypothetical `ncbi-vdb` executable. It is likely that users + intending to interact with VDB data should use a specific tool from the + SRA-toolkit like `vdb-dump` or `vdb-validate` instead. + + Args: + args: A string of command-line arguments to pass to the ncbi-vdb command. + For example, "--version" or "--help". + + Returns: + A dictionary containing the command executed, stdout, stderr, and a list of output files. + """ + command: List[str] = ["ncbi-vdb"] + if args: + command.extend(args.split()) + + command_str = " ".join(command) + log.info(f"Executing command: {command_str}") + + try: + result = subprocess.run( + command, + check=True, + capture_output=True, + text=True, + encoding='utf-8' + ) + return { + "command_executed": command_str, + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": [] + } + except FileNotFoundError: + error_message = ( + "Error: 'ncbi-vdb' executable not found. Please ensure the NCBI SRA " + "Toolkit is installed and its 'bin' directory is in the system's PATH." + ) + log.error(error_message) + return { + "command_executed": command_str, + "stdout": "", + "stderr": error_message, + "output_files": [] + } + except subprocess.CalledProcessError as e: + log.error(f"Command failed with exit code {e.returncode}") + log.error(f"Stderr: {e.stderr}") + return { + "command_executed": command_str, + "stdout": e.stdout, + "stderr": e.stderr, + "output_files": [] + } diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_ncbi-vdb/app/ncbi-vdb_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_ncbi-vdb/app/ncbi-vdb_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..ab66c1a2c1e52fe6a30947b4606c7897019a8c90 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_ncbi-vdb/app/ncbi-vdb_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_ncbi-vdb/app/ncbi-vdb_server.py') +SERVER_NAME = 'biosci_ncbi_vdb' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_ncbi-vdb/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_ncbi-vdb/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_ncbi-vdb/app/requirements.txt @@ -0,0 +1 @@ + diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_ncbi-vdb/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_ncbi-vdb/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..1cf3de8fe67fa65f840f25774a75c78332570f79 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_ncbi-vdb/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-ncbi-vdb: + build: . + image: mcp-ncbi-vdb:latest + container_name: mcp-ncbi-vdb + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=ncbi-vdb + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_ncbi-vdb/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_ncbi-vdb/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..78c24c48a28ef7a9567940e9b7f2cfbe31365a56 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_ncbi-vdb/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - ncbi-vdb + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_ncbi-vdb/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_ncbi-vdb/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_ncbi-vdb/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_ngs-smap/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_ngs-smap/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..6dbd1659e88aaad7beea9fc4b379e3cab286d7ba --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_ngs-smap/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install ngs-smap via conda (e.g., from bioconda) +RUN conda install -c bioconda ngs-smap -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY app/ngs-smap_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/ngs-smap_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/ngs-smap_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_ngs-smap/app/ngs-smap_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_ngs-smap/app/ngs-smap_server.py new file mode 100644 index 0000000000000000000000000000000000000000..004c8d00774866c25a0047d3c6b9ab189fbe16e8 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_ngs-smap/app/ngs-smap_server.py @@ -0,0 +1,136 @@ +import subprocess +from pathlib import Path +from typing import Optional, Dict, List +import logging + +# Set up logging +logging.basicConfig(level=logging.INFO) +log = logging.getLogger(__name__) + +# This is a placeholder for the MCP decorator. +# In a real MCP environment, this would be provided by the MCP framework. +class mcp: + @staticmethod + def tool(): + def decorator(func): + return func + return decorator + +@mcp.tool() +def ngs_smap( + in_file: Path, + out_file: Path, + ref: Path, + in2_file: Optional[Path] = None, + mapper: str = "bwa-mem", + threads: int = 1, + rg: Optional[str] = None, + mark_dups: bool = False, + add_umi_bamtag: bool = False, + cleanup: bool = False, + verbose: bool = False, + force: bool = False, +) -> Dict: + """ + Maps short reads to a reference genome using BWA-MEM, BWA-MEM2, or Bowtie2. + + This tool is a wrapper for different mapping tools. It requires the reference + genome to be indexed by the selected mapping tool. + + Args: + in_file: Input FASTQ file for read 1. + out_file: Output BAM file. + ref: Reference genome FASTA file. The index files are expected to be in the same folder. + in2_file: Optional input FASTQ file for read 2 (for paired-end reads). + mapper: The mapping algorithm to use. Valid options are 'bwa-mem', 'bwa-mem2', 'bowtie2'. + threads: The number of threads to use for mapping. + rg: Read group header line in SAM format (e.g., '@RG\\tID:group1\\tSM:sample1'). + mark_dups: If True, mark duplicates using Sambamba. + add_umi_bamtag: If True, adds a UMI bamtag to each alignment using fgbio. + cleanup: If True, intermediate files are deleted. + verbose: If True, enables verbose mode for more detailed output. + force: If True, forces overwriting of existing output files. + + Returns: + A dictionary containing the executed command, stdout, stderr, and a list of output files. + """ + # --- Input Validation --- + if not in_file.is_file(): + raise FileNotFoundError(f"Input file not found: {in_file}") + if not ref.is_file(): + raise FileNotFoundError(f"Reference genome file not found: {ref}") + if in2_file and not in2_file.is_file(): + raise FileNotFoundError(f"Paired-end input file not found: {in2_file}") + + allowed_mappers = {"bwa-mem", "bwa-mem2", "bowtie2"} + if mapper not in allowed_mappers: + raise ValueError(f"Invalid mapper '{mapper}'. Allowed values are: {', '.join(allowed_mappers)}") + + if threads < 1: + raise ValueError("Number of threads must be at least 1.") + + # Ensure output directory exists + try: + out_file.parent.mkdir(parents=True, exist_ok=True) + except Exception as e: + raise IOError(f"Could not create output directory {out_file.parent}: {e}") + + # --- Command Construction --- + cmd = [ + "ngs-smap", + "-in", str(in_file), + "-out", str(out_file), + "-ref", str(ref), + "-mapper", mapper, + "-threads", str(threads), + ] + + if in2_file: + cmd.extend(["-in2", str(in2_file)]) + + if rg: + cmd.extend(["-rg", rg]) + + # Add boolean flags if they are set to True + if mark_dups: + cmd.append("-mark_dups") + if add_umi_bamtag: + cmd.append("-add_umi_bamtag") + if cleanup: + cmd.append("-cleanup") + if verbose: + cmd.append("-v") + if force: + cmd.append("--force") + + command_executed = " ".join(cmd) + log.info(f"Executing command: {command_executed}") + + # --- Subprocess Execution --- + try: + result = subprocess.run( + cmd, + capture_output=True, + text=True, + check=True, + ) + + output_files = [str(out_file)] if out_file.exists() else [] + + return { + "command_executed": command_executed, + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": output_files + } + except FileNotFoundError: + error_message = "Error: 'ngs-smap' command not found. Please ensure the ngs-bits suite is installed and in your system's PATH." + log.error(error_message) + # Raising an exception is often better for MCP tools to signal a setup/environment issue + raise RuntimeError(error_message) + except subprocess.CalledProcessError as e: + log.error(f"ngs-smap execution failed with return code {e.returncode}") + log.error(f"STDOUT: {e.stdout}") + log.error(f"STDERR: {e.stderr}") + # Propagate the error information in a structured way + raise RuntimeError(f"ngs-smap failed: {e.stderr}") from e \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_ngs-smap/app/ngs-smap_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_ngs-smap/app/ngs-smap_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..71ea2a1f76274f9b2950314615ec8a128a0aed13 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_ngs-smap/app/ngs-smap_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_ngs-smap/app/ngs-smap_server.py') +SERVER_NAME = 'biosci_ngs_smap' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_ngs-smap/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_ngs-smap/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_ngs-smap/app/requirements.txt @@ -0,0 +1 @@ + diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_ngs-smap/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_ngs-smap/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..68182a90ff75f92915bea2caf8d0465d13f95361 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_ngs-smap/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-ngs-smap: + build: . + image: mcp-ngs-smap:latest + container_name: mcp-ngs-smap + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=ngs-smap + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_ngs-smap/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_ngs-smap/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..694457530fe19fa211a3019510f80bbf4835218f --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_ngs-smap/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - ngs-smap + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_ngs-smap/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_ngs-smap/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_ngs-smap/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_opticlust/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_opticlust/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..cbc5b0d1e1178444c116ef406ddbc44ac6db12d1 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_opticlust/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install opticlust via conda (e.g., from bioconda) +RUN conda install -c bioconda opticlust -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY app/opticlust_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/opticlust_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/opticlust_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_opticlust/app/opticlust_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_opticlust/app/opticlust_server.py new file mode 100644 index 0000000000000000000000000000000000000000..ca3229e03d27604c2f76db3f9b2460c9491eb4f5 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_opticlust/app/opticlust_server.py @@ -0,0 +1,377 @@ +import logging +import subprocess +from pathlib import Path +from typing import Optional, Dict, Any + +# Configure logging +logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') + +# Since opticlust is a library, we will import its functions directly. +# These imports would be placed within the execution environment. +try: + import numpy as np + import scanpy as sc + import matplotlib.pyplot as plt + from opticlust.clust import clustering as opticlust_clustering_func + from opticlust.clust import clustering_plot as opticlust_plot_func + from opticlust.recommend import recommend_resolutions as opticlust_recommend_func + from opticlust.recommend import score_resolutions as opticlust_score_func + from opticlust.tree import clustree as opticlust_clustree_func +except ImportError: + # This allows the file to be parsed without the libraries installed. + # The MCP runner will handle the actual environment setup. + pass + +# @mcp.tool() decorator would be used in a real MCP environment. + +def opticlust_clustering( + input_adata: Path, + output_adata: Path, + flavor: str = "leiden", + min_res: float = 0.1, + max_res: float = 2.0, + n_res: int = 20, + random_state: int = 0, + key_added: str = "opticlust", + n_neighbors: int = 15, + n_pcs: Optional[int] = None, +) -> Dict[str, Any]: + """ + Performs clustering on a single-cell dataset at various resolutions using opticlust. + + This function wraps the clustering functionality of opticlust, iterating through a range of + resolutions to generate multiple clustering solutions. It uses scanpy's neighborhood graph + and either the Leiden or Louvain algorithm. + + Args: + input_adata: Path to the input AnnData object file (.h5ad). + output_adata: Path to save the output AnnData object with clustering results. + flavor: Clustering algorithm to use. Must be 'leiden' or 'louvain'. + min_res: The minimum resolution for clustering. + max_res: The maximum resolution for clustering. + n_res: The number of resolutions to test between min_res and max_res. + random_state: Seed for the random number generator for reproducibility. + key_added: Prefix for the columns added to `adata.obs`. + n_neighbors: Number of neighbors to use for building the neighborhood graph. + n_pcs: Number of principal components to use for the neighborhood graph. If None, + uses existing PCA in `adata.obsm['X_pca']`. + + Returns: + A dictionary containing the command executed, stdout, stderr, the path to the + output AnnData file, and the list of new column names added. + """ + # Input validation + if not input_adata.is_file(): + raise FileNotFoundError(f"Input file not found: {input_adata}") + if flavor not in ["leiden", "louvain"]: + raise ValueError("`flavor` must be either 'leiden' or 'louvain'.") + if min_res >= max_res: + raise ValueError("`min_res` must be less than `max_res`.") + if n_res <= 0: + raise ValueError("`n_res` must be a positive integer.") + + output_adata.parent.mkdir(parents=True, exist_ok=True) + + command_str = ( + f"opticlust.clust.clustering(adata, flavor='{flavor}', min_res={min_res}, " + f"max_res={max_res}, n_res={n_res}, random_state={random_state}, " + f"key_added='{key_added}', n_neighbors={n_neighbors}, n_pcs={n_pcs})" + ) + + try: + adata = sc.read_h5ad(input_adata) + + if 'neighbors' not in adata.uns: + logging.info("Neighborhood graph not found. Computing neighbors...") + sc.pp.neighbors(adata, n_neighbors=n_neighbors, n_pcs=n_pcs) + + resolutions = np.linspace(min_res, max_res, n_res) + cluster_kwargs = { + "flavor": flavor, + "resolution": resolutions, + "random_state": random_state, + "key_added": key_added, + } + + new_columns = opticlust_clustering_func(adata, cluster_kwargs=cluster_kwargs) + adata.write_h5ad(output_adata) + + return { + "command_executed": command_str, + "stdout": f"Successfully performed clustering. Added columns: {new_columns}", + "stderr": "", + "output_files": {"output_adata": str(output_adata)}, + "new_columns": new_columns + } + except Exception as e: + logging.error(f"Error during opticlust clustering: {e}") + return { + "command_executed": command_str, + "stdout": "", + "stderr": str(e), + "output_files": {}, + } + +def opticlust_score_resolutions( + input_adata: Path, + output_adata: Path, + columns: str, + tests: str = "SH_CH_DB", + method: str = "mean", +) -> Dict[str, Any]: + """ + Scores clustering resolutions using silhouette, Calinski-Harabasz, and Davies-Bouldin scores. + + This function updates the `adata.uns['opticlust']` dictionary with the calculated scores + for each specified clustering resolution. + + Args: + input_adata: Path to an AnnData object file (.h5ad) with clustering results. + output_adata: Path to save the output AnnData object with scores. + columns: Comma-separated string of column names in `adata.obs` to be scored. + tests: A string indicating which tests to run. 'S' for Silhouette, 'H' for + Calinski-Harabasz, 'D' for Davies-Bouldin. + method: Method to combine scores ('mean' or 'median'). + + Returns: + A dictionary containing the command executed, stdout, stderr, and the path to the + output AnnData file. + """ + if not input_adata.is_file(): + raise FileNotFoundError(f"Input file not found: {input_adata}") + if not columns: + raise ValueError("`columns` parameter cannot be empty.") + if method not in ["mean", "median"]: + raise ValueError("`method` must be either 'mean' or 'median'.") + + output_adata.parent.mkdir(parents=True, exist_ok=True) + column_list = [c.strip() for c in columns.split(',')] + + command_str = ( + f"opticlust.recommend.score_resolutions(adata, columns={column_list}, " + f"tests='{tests}', method='{method}')" + ) + + try: + adata = sc.read_h5ad(input_adata) + + for col in column_list: + if col not in adata.obs.columns: + raise ValueError(f"Column '{col}' not found in input AnnData object.") + + opticlust_score_func(adata, column_list, tests=tests, method=method) + adata.write_h5ad(output_adata) + + return { + "command_executed": command_str, + "stdout": "Successfully scored resolutions. Updated adata.uns['opticlust'].", + "stderr": "", + "output_files": {"output_adata": str(output_adata)} + } + except Exception as e: + logging.error(f"Error during opticlust scoring: {e}") + return { + "command_executed": command_str, + "stdout": "", + "stderr": str(e), + "output_files": {}, + } + +def opticlust_clustering_plot( + input_adata: Path, + output_plot: Path, + columns: str, + method: str = "score", + min_n_resolutions: int = 1, +) -> Dict[str, Any]: + """ + Generates a plot of clustering scores vs. number of clusters and pre-selects promising resolutions. + + Args: + input_adata: Path to a scored AnnData object file (.h5ad). + output_plot: Path to save the output plot (e.g., .png, .svg). + columns: Comma-separated string of column names in `adata.obs` to be plotted. + method: Method used for plotting. Typically 'score'. + min_n_resolutions: Minimum number of resolutions to be selected and returned. + + Returns: + A dictionary containing the command executed, the path to the output plot file, + and the list of selected (promising) column names. + """ + if not input_adata.is_file(): + raise FileNotFoundError(f"Input file not found: {input_adata}") + if not columns: + raise ValueError("`columns` parameter cannot be empty.") + + output_plot.parent.mkdir(parents=True, exist_ok=True) + column_list = [c.strip() for c in columns.split(',')] + + command_str = ( + f"opticlust.clust.clustering_plot(adata, columns={column_list}, " + f"method='{method}', min_n_resolutions={min_n_resolutions})" + ) + + try: + adata = sc.read_h5ad(input_adata) + + for col in column_list: + if col not in adata.obs.columns: + raise ValueError(f"Column '{col}' not found in input AnnData object.") + + if 'opticlust' not in adata.uns: + raise ValueError("Scoring info not found in `adata.uns['opticlust']`. " + "Run `opticlust_score_resolutions` first.") + + plt.figure() + selected_columns = opticlust_plot_func( + adata, column_list, method=method, min_n_resolutions=min_n_resolutions + ) + plt.savefig(output_plot, bbox_inches='tight', dpi=150) + plt.close() + + return { + "command_executed": command_str, + "stdout": f"Plot generated successfully. Selected columns: {selected_columns}", + "stderr": "", + "output_files": {"plot": str(output_plot)}, + "selected_columns": selected_columns + } + except Exception as e: + logging.error(f"Error during opticlust plotting: {e}") + return { + "command_executed": command_str, + "stdout": "", + "stderr": str(e), + "output_files": {}, + } + +def opticlust_recommend_resolutions( + input_adata: Path, + columns: str, +) -> Dict[str, Any]: + """ + Recommends the best clustering resolution from low, medium, and high stability bins. + + This function analyzes scores in `adata.uns['opticlust']` and identifies the top + overall resolution, as well as top resolutions within three cluster-count bins. + + Args: + input_adata: Path to a scored AnnData object file (.h5ad). + columns: Comma-separated string of column names to consider for recommendations. + Typically the output from `opticlust_clustering_plot`. + + Returns: + A dictionary containing the command executed and the recommended resolution column names. + """ + if not input_adata.is_file(): + raise FileNotFoundError(f"Input file not found: {input_adata}") + if not columns: + raise ValueError("`columns` parameter cannot be empty.") + + column_list = [c.strip() for c in columns.split(',')] + command_str = f"opticlust.recommend.recommend_resolutions(adata, columns={column_list})" + + try: + adata = sc.read_h5ad(input_adata) + + for col in column_list: + if col not in adata.obs.columns: + raise ValueError(f"Column '{col}' not found in input AnnData object.") + + if 'opticlust' not in adata.uns: + raise ValueError("Scoring info not found in `adata.uns['opticlust']`. " + "Run `opticlust_score_resolutions` first.") + + top_overall, top_low, top_medium, top_high = opticlust_recommend_func(adata, column_list) + + return { + "command_executed": command_str, + "stdout": "Successfully recommended resolutions.", + "stderr": "", + "recommendations": { + "top_overall": top_overall, + "top_low": top_low, + "top_medium": top_medium, + "top_high": top_high, + } + } + except Exception as e: + logging.error(f"Error during opticlust recommendation: {e}") + return { + "command_executed": command_str, + "stdout": "", + "stderr": str(e), + "recommendations": {}, + } + +def opticlust_clustree( + input_adata: Path, + output_adata: Path, + output_plot: Path, + columns: str, + rename_clusters: bool = True, +) -> Dict[str, Any]: + """ + Builds and plots a clustering tree to visualize the relationship between resolutions. + + This function can also rename clusters for consistency across resolutions, which simplifies + downstream analysis and visualization. + + Args: + input_adata: Path to an AnnData object file (.h5ad) with clustering results. + output_adata: Path to save the output AnnData object (potentially with renamed clusters). + output_plot: Path to save the output clustree plot (e.g., .png, .svg). + columns: Comma-separated string of column names to include in the tree. + rename_clusters: If True, rename clusters to be consistent across resolutions. + + Returns: + A dictionary containing the command executed, paths to the output AnnData and plot files. + """ + if not input_adata.is_file(): + raise FileNotFoundError(f"Input file not found: {input_adata}") + if not columns: + raise ValueError("`columns` parameter cannot be empty.") + + output_adata.parent.mkdir(parents=True, exist_ok=True) + output_plot.parent.mkdir(parents=True, exist_ok=True) + column_list = [c.strip() for c in columns.split(',')] + + command_str = ( + f"opticlust.tree.clustree(adata, columns={column_list}, " + f"rename_clusters={rename_clusters})" + ) + + try: + adata = sc.read_h5ad(input_adata) + + for col in column_list: + if col not in adata.obs.columns: + raise ValueError(f"Column '{col}' not found in input AnnData object.") + + g = opticlust_clustree_func(adata, column_list, rename_clusters=rename_clusters) + + g.draw(output_plot, prog='dot') + adata.write_h5ad(output_adata) + + return { + "command_executed": command_str, + "stdout": "Clustree plot generated and AnnData object updated successfully.", + "stderr": "", + "output_files": { + "output_adata": str(output_adata), + "plot": str(output_plot), + } + } + except Exception as e: + error_msg = str(e) + if "failed to execute" in error_msg.lower() and "dot" in error_msg.lower(): + error_msg += ("\nThis error often indicates that the 'graphviz' package is not installed " + "or not in the system's PATH. Please install it (e.g., 'conda install graphviz').") + + logging.error(f"Error during opticlust clustree generation: {error_msg}") + return { + "command_executed": command_str, + "stdout": "", + "stderr": error_msg, + "output_files": {}, + } \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_opticlust/app/opticlust_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_opticlust/app/opticlust_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..c69cedcbe4e32ee7f9f4a67fbe64ef48315c8b9d --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_opticlust/app/opticlust_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_opticlust/app/opticlust_server.py') +SERVER_NAME = 'biosci_opticlust' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_opticlust/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_opticlust/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..04df1ef21e18c3e09c6db9669a6854499f226b44 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_opticlust/app/requirements.txt @@ -0,0 +1,4 @@ +matplotlib==3.10.9 +numpy==2.4.4 +opticlust==0.5.0 +scanpy==1.12.1 diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_opticlust/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_opticlust/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..55c7f26ed27cc955791acc32c314c0e176eeb10a --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_opticlust/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-opticlust: + build: . + image: mcp-opticlust:latest + container_name: mcp-opticlust + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=opticlust + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_opticlust/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_opticlust/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..4658dd5df9df3a590c8c52c9b43875be87e53159 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_opticlust/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - opticlust + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_opticlust/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_opticlust/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_opticlust/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_orthofinder/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_orthofinder/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..00c5dbe3e58a49c4f6bc1cc6243a9dcc54d9fe77 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_orthofinder/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install orthofinder via conda (e.g., from bioconda) +RUN conda install -c bioconda orthofinder -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY app/orthofinder_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/orthofinder_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/orthofinder_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_orthofinder/app/orthofinder_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_orthofinder/app/orthofinder_server.py new file mode 100644 index 0000000000000000000000000000000000000000..adc48608692edb291e950b438b45eb43d2c73ecd --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_orthofinder/app/orthofinder_server.py @@ -0,0 +1,199 @@ +import subprocess +import logging +from pathlib import Path +from typing import Optional, List +import tempfile +import os + +# It is assumed that 'mcp' is available in the execution environment. +# Since we are only providing the function code, we will not import it here. +# from mcp import tool as mcp_tool + +# Setup basic logging +logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') + +@mcp.tool() +def run_orthofinder( + fasta_dir: Path, + output_dir: Optional[Path] = None, + species_tree: Optional[Path] = None, + mcl_inflation: float = 1.5, + threads: int = 16, + analysis_threads: int = 1, + is_dna: bool = False, + search_program: str = "diamond", + msa_program: str = "muscle", + tree_inference_program: str = "fasttree", + no_species_names: bool = False, + previous_results_dir: Optional[Path] = None, + orthogroup_inference_only: bool = False, + resume: bool = False, + species_tree_inference_program: str = "fasttree", + citations_file: Optional[Path] = None, + run_name: Optional[str] = None, + clean_intermediate: bool = False, + help_flag: bool = False, + version_flag: bool = False +) -> dict: + """ + Runs OrthoFinder, a tool for comparative genomics. + + This function wraps the main `orthofinder` command, providing a Pythonic interface + to its comprehensive set of features for finding orthogroups, orthologs, and inferring + gene and species trees. + + Args: + fasta_dir: Directory containing protein or DNA sequence files in FASTA format, one file per species. + output_dir: Directory to save the results. If not provided, a default directory will be created by OrthoFinder. + species_tree: Path to a user-provided species tree file. + mcl_inflation: The MCL inflation parameter (-I). Controls the granularity of orthogroup clustering. + threads: Number of parallel sequence search threads to use (-t). + analysis_threads: Number of parallel analysis threads to use (-a). + is_dna: Set to True if the input sequences are DNA, not protein (-d). + search_program: The sequence search program to use (-S). Options: 'diamond', 'diamond_ultra_sens', 'blast', 'blast_gz', 'mmseqs'. + msa_program: The multiple sequence alignment program to use (-msa). Options: 'muscle', 'mafft'. + tree_inference_program: The gene tree inference program to use (-T). Options: 'fasttree', 'raxml-ng', 'iqtree'. + no_species_names: If True, do not add species names to sequence IDs (-X). + previous_results_dir: Path to a directory of previous OrthoFinder results to reuse (-fg). + orthogroup_inference_only: If True, perform orthogroup inference only, requires a previous run (-og). + resume: If True, start from the previous point in an incomplete run (-op). + species_tree_inference_program: The tree inference program for the species tree (-Z). Options: 'fasttree', 'raxml-ng', 'iqtree'. + citations_file: Path to a file to write citations to (-c). + run_name: A name for this run, used for the results directory if `output_dir` is not set (-n). + clean_intermediate: If True, delete intermediate files to save space (-clean). + help_flag: If True, display the help message and exit (-h). + version_flag: If True, print the version number and exit (-v). + + Returns: + A dictionary containing the execution command, stdout, stderr, and a list of output files. + """ + # --- 1. Input Validation --- + if not Path("orthofinder").is_file() and not any(os.access(os.path.join(path, 'orthofinder'), os.X_OK) for path in os.environ["PATH"].split(os.pathsep)): + raise FileNotFoundError("orthofinder executable not found in PATH.") + + cmd = ["orthofinder"] + + if help_flag: + cmd.append("-h") + elif version_flag: + cmd.append("-v") + else: + # Validate required inputs for a normal run + if not fasta_dir.is_dir(): + raise ValueError(f"Input FASTA directory '{fasta_dir}' does not exist or is not a directory.") + + # Validate parameter choices + valid_search_programs = ["diamond", "diamond_ultra_sens", "blast", "blast_gz", "mmseqs"] + if search_program not in valid_search_programs: + raise ValueError(f"Invalid search_program '{search_program}'. Must be one of {valid_search_programs}.") + + valid_msa_programs = ["muscle", "mafft", "muscle3"] + if msa_program not in valid_msa_programs: + raise ValueError(f"Invalid msa_program '{msa_program}'. Must be one of {valid_msa_programs}.") + + valid_tree_programs = ["fasttree", "raxml-ng", "iqtree"] + if tree_inference_program not in valid_tree_programs: + raise ValueError(f"Invalid tree_inference_program '{tree_inference_program}'. Must be one of {valid_tree_programs}.") + if species_tree_inference_program not in valid_tree_programs: + raise ValueError(f"Invalid species_tree_inference_program '{species_tree_inference_program}'. Must be one of {valid_tree_programs}.") + + # Validate conditional requirements + if orthogroup_inference_only and not previous_results_dir: + raise ValueError("`previous_results_dir` must be provided when `orthogroup_inference_only` is True.") + + # --- 2. Command Construction --- + cmd.extend(["-f", str(fasta_dir)]) + cmd.extend(["-S", search_program]) + cmd.extend(["-M", search_program]) # Also set legacy -M for compatibility + cmd.extend(["-msa", msa_program]) + cmd.extend(["-A", msa_program]) # Also set legacy -A for compatibility + cmd.extend(["-T", tree_inference_program]) + cmd.extend(["-Z", species_tree_inference_program]) + cmd.extend(["-I", str(mcl_inflation)]) + cmd.extend(["-t", str(threads)]) + cmd.extend(["-a", str(analysis_threads)]) + + if output_dir: + output_dir.mkdir(parents=True, exist_ok=True) + cmd.extend(["-o", str(output_dir)]) + + if species_tree: + if not species_tree.is_file(): + raise FileNotFoundError(f"Species tree file '{species_tree}' not found.") + cmd.extend(["-s", str(species_tree)]) + + if is_dna: + cmd.append("-d") + if no_species_names: + cmd.append("-X") + if previous_results_dir: + if not previous_results_dir.is_dir(): + raise FileNotFoundError(f"Previous results directory '{previous_results_dir}' not found.") + cmd.extend(["-fg", str(previous_results_dir)]) + if orthogroup_inference_only: + cmd.append("-og") + if resume: + cmd.append("-op") + if citations_file: + cmd.extend(["-c", str(citations_file)]) + if run_name: + cmd.extend(["-n", run_name]) + if clean_intermediate: + cmd.append("-clean") + + # --- 3. Subprocess Execution --- + command_str = " ".join(cmd) + logging.info(f"Executing command: {command_str}") + + try: + result = subprocess.run( + cmd, + capture_output=True, + text=True, + check=True + ) + stdout = result.stdout + stderr = result.stderr + logging.info("OrthoFinder executed successfully.") + + except subprocess.CalledProcessError as e: + logging.error(f"OrthoFinder failed with exit code {e.returncode}.") + logging.error(f"Stderr: {e.stderr}") + logging.error(f"Stdout: {e.stdout}") + return { + "command_executed": command_str, + "stdout": e.stdout, + "stderr": e.stderr, + "output_files": [] + } + except FileNotFoundError: + logging.error("orthofinder command not found. Please ensure it is in your system's PATH.") + return { + "command_executed": command_str, + "stdout": "", + "stderr": "orthofinder command not found. Please ensure it is in your system's PATH.", + "output_files": [] + } + + # --- 4. Structured Result Return --- + output_files = [] + final_output_dir = output_dir + if not final_output_dir: + # OrthoFinder creates a default dir, we need to find it from stdout + for line in stdout.splitlines(): + if "Results directory:" in line: + path_str = line.split("Results directory:")[1].strip() + final_output_dir = Path(path_str) + break + + if final_output_dir and final_output_dir.is_dir(): + for root, _, files in os.walk(final_output_dir): + for name in files: + output_files.append(str(Path(root) / name)) + + return { + "command_executed": command_str, + "stdout": stdout, + "stderr": stderr, + "output_files": output_files + } \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_orthofinder/app/orthofinder_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_orthofinder/app/orthofinder_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..26f3a35247cb680be99d5fa14ef13c7e6596b0c3 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_orthofinder/app/orthofinder_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_orthofinder/app/orthofinder_server.py') +SERVER_NAME = 'biosci_orthofinder' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_orthofinder/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_orthofinder/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_orthofinder/app/requirements.txt @@ -0,0 +1 @@ + diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_orthofinder/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_orthofinder/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..cd6a20861627cacb2346876609ce392eaadf1b5f --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_orthofinder/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-orthofinder: + build: . + image: mcp-orthofinder:latest + container_name: mcp-orthofinder + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=orthofinder + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_orthofinder/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_orthofinder/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..148b0f16fc688842bcebff628aadd4c80d15bb19 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_orthofinder/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - orthofinder + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_orthofinder/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_orthofinder/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_orthofinder/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_parasail-python/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_parasail-python/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..f02664f0502a44bcd1fa9cc10f243539788a8c38 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_parasail-python/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install parasail-python via conda (e.g., from bioconda) +RUN conda install -c bioconda parasail-python -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY app/parasail-python_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/parasail-python_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/parasail-python_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_parasail-python/app/parasail-python_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_parasail-python/app/parasail-python_server.py new file mode 100644 index 0000000000000000000000000000000000000000..e43192e2c5daada29aa0547b96a69173bbe5175e --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_parasail-python/app/parasail-python_server.py @@ -0,0 +1,168 @@ +import subprocess +from pathlib import Path +from typing import Dict, Any, List +import logging + +# Set up logging +logging.basicConfig(level=logging.INFO) +logger = logging.getLogger(__name__) + +# MCP decorator placeholder, as per instructions. +# In a real MCP environment, this would be `from mcp import tool`. +class mcp: + @staticmethod + def tool(): + def decorator(f): + return f + return decorator + +@mcp.tool() +def parasail_aligner( + a_file: Path, + b_file: Path, + function: str = "sw_trace_striped_sat", + matrix: str = "blosum62", + open_penalty: int = 10, + extend_penalty: int = 1, + threads: int = 1, + xml: bool = False, + header: bool = False, + profile: bool = False, + stats: bool = False, + is_global: bool = False, + semi_global: bool = False, + diag: bool = False, + scan: bool = False, + nogap: bool = False, + saturated: bool = False, + non_saturated: bool = False, + print_score_matrix: bool = False, +) -> Dict[str, Any]: + """ + Performs pairwise sequence alignment using the parasail library command-line tool. + + This tool is a wrapper for the 'parasail_aligner' command-line utility, + which provides access to the SIMD-accelerated pairwise alignment functions + of the parasail library. It aligns sequences from two FASTA files. + + Args: + a_file: Path to the first sequence file (FASTA format). + b_file: Path to the second sequence file (FASTA format). + function: The alignment function to use (e.g., 'sw_trace_striped_sat'). + matrix: The substitution matrix to use (e.g., 'blosum62') or path to a matrix file. + open_penalty: The penalty for opening a gap. + extend_penalty: The penalty for extending a gap. + threads: The number of threads to use for the computation. + xml: If True, output results in XML format. + header: If True, include a header in the output. + profile: If True, use profile alignment. + stats: If True, use stats alignment. + is_global: If True, perform a global alignment (Needleman-Wunsch). + semi_global: If True, perform a semi-global alignment. + diag: If True, perform a diagonal alignment. + scan: If True, perform a scan alignment. + nogap: If True, perform a nogap alignment. + saturated: If True, use a saturated implementation. + non_saturated: If True, use a non-saturated implementation. + print_score_matrix: If True, print the score matrix in the output. + + Returns: + A dictionary containing the command executed, stdout, stderr, and a list of output files. + """ + # 1. Input Validation + if not a_file.is_file(): + raise FileNotFoundError(f"Input file 'a_file' not found at {a_file}") + if not b_file.is_file(): + raise FileNotFoundError(f"Input file 'b_file' not found at {b_file}") + + if open_penalty < 0: + raise ValueError("open_penalty must be a non-negative integer.") + if extend_penalty < 0: + raise ValueError("extend_penalty must be a non-negative integer.") + if threads <= 0: + raise ValueError("threads must be a positive integer.") + + if saturated and non_saturated: + raise ValueError("Cannot specify both saturated (-z) and non-saturated (-n) implementations simultaneously.") + + alignment_modes = { + "global": is_global, + "semi_global": semi_global, + "diag": diag, + "scan": scan, + "nogap": nogap, + } + selected_modes = [mode for mode, selected in alignment_modes.items() if selected] + if len(selected_modes) > 1: + raise ValueError( + f"Only one alignment mode can be selected at a time. Found: {', '.join(selected_modes)}" + ) + + # 2. Command Construction + cmd: List[str] = [ + "parasail_aligner", + "-a", str(a_file), + "-b", str(b_file), + "-f", function, + "-m", matrix, + "-o", str(open_penalty), + "-e", str(extend_penalty), + "-t", str(threads), + ] + + if xml: cmd.append("-x") + if header: cmd.append("-c") + if profile: cmd.append("-p") + if stats: cmd.append("-s") + if is_global: cmd.append("-l") + if semi_global: cmd.append("-i") + if diag: cmd.append("-d") + if scan: cmd.append("-w") + if nogap: cmd.append("-g") + if saturated: cmd.append("-z") + if non_saturated: cmd.append("-n") + if print_score_matrix: cmd.append("-r") + + command_executed = " ".join(cmd) + logger.info(f"Executing command: {command_executed}") + + # 3. Subprocess Execution and Error Handling + try: + result = subprocess.run( + cmd, + capture_output=True, + text=True, + check=True, + ) + except FileNotFoundError: + error_message = "The 'parasail_aligner' executable was not found. Please ensure it is installed and in your system's PATH." + logger.error(error_message) + # Return a structured error that conforms to MCP expectations + return { + "error": error_message, + "command_executed": command_executed, + "stdout": "", + "stderr": "", + "return_code": 127, + "output_files": [] + } + except subprocess.CalledProcessError as e: + logger.error(f"Parasail aligner failed with exit code {e.returncode}") + logger.error(f"Stderr: {e.stderr}") + return { + "error": "Parasail aligner failed with a non-zero exit code.", + "return_code": e.returncode, + "command_executed": command_executed, + "stdout": e.stdout, + "stderr": e.stderr, + "output_files": [] + } + + # 4. Structured Result Return + # parasail_aligner prints results to stdout, so there are no explicit output files. + return { + "command_executed": command_executed, + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": [], + } \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_parasail-python/app/parasail-python_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_parasail-python/app/parasail-python_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..7a0bcd0764d6a62ae41dceb9343f9f16a01a045d --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_parasail-python/app/parasail-python_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_parasail-python/app/parasail-python_server.py') +SERVER_NAME = 'biosci_parasail_python' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_parasail-python/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_parasail-python/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_parasail-python/app/requirements.txt @@ -0,0 +1 @@ + diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_parasail-python/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_parasail-python/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..cf00e0fd87375604bb47bfb3228d473761e5dcb9 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_parasail-python/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-parasail-python: + build: . + image: mcp-parasail-python:latest + container_name: mcp-parasail-python + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=parasail-python + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_parasail-python/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_parasail-python/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..9816ad719203c7f825b022d954d3993432361214 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_parasail-python/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - parasail-python + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_parasail-python/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_parasail-python/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_parasail-python/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_pasta/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_pasta/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..0278c4e0a58a47e385a3444c112296d1084a4840 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_pasta/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install pasta via conda (e.g., from bioconda) +RUN conda install -c bioconda pasta -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY app/pasta_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/pasta_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/pasta_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_pasta/app/pasta_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_pasta/app/pasta_server.py new file mode 100644 index 0000000000000000000000000000000000000000..c1c6dbc370178c6b06ba9dafc99856719b9946f7 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_pasta/app/pasta_server.py @@ -0,0 +1,155 @@ +import subprocess +import tempfile +from pathlib import Path +from typing import List, Optional, Dict, Any + +# Assume 'pasta' is the executable name available in the system's PATH. +# If it's a Python script (e.g., run_pasta.py), the command would need to be +# `python run_pasta.py ...` and `pasta_executable` would be `["python", "run_pasta.py"]`. +PASTA_EXECUTABLE = "pasta" + + +@mcp.tool() +def align_sequences( + input_fasta: Path, + output_prefix: str, + num_iterations: int = 3, + threads: int = 1, + memory_limit: Optional[str] = None, + temp_dir: Optional[Path] = None, + keep_intermediate: bool = False, + verbose: bool = False, + force_overwrite: bool = False, +) -> Dict[str, Any]: + """ + Performs multiple sequence alignment using the PASTA (Practical Alignment using SATé and MAFFT) tool. + + PASTA is designed for large-scale phylogenetic inference by providing accurate + multiple sequence alignments. + + Args: + input_fasta: Path to the input FASTA file containing unaligned sequences. + This file must exist. + output_prefix: A prefix for the output files. PASTA will generate files + like .fasta (the alignment), .log, etc. + The output files will be placed in the current working directory + or a specified temporary directory if `temp_dir` is used. + num_iterations: Number of iterations for the PASTA algorithm. + Defaults to 3. Must be a positive integer. + threads: Number of threads (CPU cores) to use for parallel processing. + Defaults to 1. Must be a positive integer. + memory_limit: Memory limit for PASTA, e.g., "4G" for 4 gigabytes. + If not specified, PASTA will use available memory. + temp_dir: Optional path to a directory for temporary files. If not provided, + PASTA will use its default temporary directory behavior, which + might be in the current working directory or a system default. + If provided, the directory must exist. + keep_intermediate: If True, PASTA will keep intermediate files generated + during the alignment process. Defaults to False. + verbose: If True, PASTA will print more detailed progress information to stderr. + Defaults to False. + force_overwrite: If True, PASTA will overwrite existing output files + without prompting. Defaults to False. + + Returns: + A dictionary containing: + - command_executed (str): The full command string executed. + - stdout (str): Standard output from the PASTA command. + - stderr (str): Standard error from the PASTA command. + - output_files (List[Path]): A list of paths to the generated output files. + + Raises: + FileNotFoundError: If the input_fasta file does not exist. + ValueError: If num_iterations or threads are not positive integers, + or if output_prefix is empty. + subprocess.CalledProcessError: If the PASTA command returns a non-zero exit code. + """ + # Input validation + if not input_fasta.is_file(): + raise FileNotFoundError(f"Input FASTA file not found: {input_fasta}") + if not input_fasta.suffix.lower() in [".fasta", ".fa", ".fna", ".fas"]: + # This is a common convention, but PASTA might accept others. + # Adding a warning or strict check can be useful. + pass # For now, just a soft check. + + if not isinstance(num_iterations, int) or num_iterations <= 0: + raise ValueError(f"num_iterations must be a positive integer, got {num_iterations}") + if not isinstance(threads, int) or threads <= 0: + raise ValueError(f"threads must be a positive integer, got {threads}") + if not output_prefix: + raise ValueError("output_prefix cannot be an empty string.") + + if temp_dir: + if not temp_dir.is_dir(): + raise FileNotFoundError(f"Temporary directory not found: {temp_dir}") + + command = [ + PASTA_EXECUTABLE, + "-i", str(input_fasta), + "-o", output_prefix, + "-j", str(num_iterations), + "-t", str(threads), + ] + + if memory_limit: + command.extend(["-m", memory_limit]) + if temp_dir: + command.extend(["--tempdir", str(temp_dir)]) + if keep_intermediate: + command.append("--keep-intermediate") + if verbose: + command.append("--verbose") + if force_overwrite: + command.append("--force") + + stdout: str = "" + stderr: str = "" + output_files: List[Path] = [] + + try: + process = subprocess.run( + command, + check=True, + capture_output=True, + text=True, + cwd=temp_dir if temp_dir else None # Run in temp_dir if specified, otherwise current dir + ) + stdout = process.stdout + stderr = process.stderr + + # PASTA typically outputs .fasta and .log + # The actual output directory depends on `cwd` and `temp_dir`. + # If temp_dir is used, output files are relative to temp_dir. + # If not, they are relative to the current working directory. + output_dir = temp_dir if temp_dir else Path.cwd() + + aligned_fasta = output_dir / f"{output_prefix}.fasta" + log_file = output_dir / f"{output_prefix}.log" + + if aligned_fasta.is_file(): + output_files.append(aligned_fasta) + if log_file.is_file(): + output_files.append(log_file) + + # PASTA might generate other files depending on its internal workings + # (e.g., intermediate trees, alignment parts). + # A more robust approach might involve parsing the log or checking common patterns. + # For now, we'll just capture the primary expected outputs. + + except FileNotFoundError: + stderr = f"Error: PASTA executable '{PASTA_EXECUTABLE}' not found. " \ + "Please ensure it is installed and available in your system's PATH." + raise + except subprocess.CalledProcessError as e: + stderr = f"PASTA command failed with exit code {e.returncode}:\n{e.stderr}" + raise + except Exception as e: + stderr = f"An unexpected error occurred: {e}" + raise + + return { + "command_executed": " ".join(command), + "stdout": stdout, + "stderr": stderr, + "output_files": output_files, + } \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_pasta/app/pasta_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_pasta/app/pasta_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..a0e9694db4ac92a052d2c4b4f161fbebf19d969f --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_pasta/app/pasta_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_pasta/app/pasta_server.py') +SERVER_NAME = 'biosci_pasta' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_pasta/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_pasta/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_pasta/app/requirements.txt @@ -0,0 +1 @@ + diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_pasta/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_pasta/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..28a05ccd2e1681c6ed6ad2f0efe59a330b16e21d --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_pasta/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-pasta: + build: . + image: mcp-pasta:latest + container_name: mcp-pasta + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=pasta + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_pasta/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_pasta/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..d41738c1a08fc6ad77c77945f3505012ecb290b5 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_pasta/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - pasta + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_pasta/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_pasta/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_pasta/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-bio-phylo/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-bio-phylo/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-bio-phylo/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-bioperl-core/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-bioperl-core/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..107c01e9906e11c89598b792cf00b9597b6aa7e0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-bioperl-core/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install perl-bioperl-core via conda (e.g., from bioconda) +RUN conda install -c bioconda perl-bioperl-core -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY app/perl-bioperl-core_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/perl-bioperl-core_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/perl-bioperl-core_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-bioperl-core/app/perl-bioperl-core_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-bioperl-core/app/perl-bioperl-core_server.py new file mode 100644 index 0000000000000000000000000000000000000000..ffbf4d7fdee1a07c0eff1b7f9341c5977a025e93 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-bioperl-core/app/perl-bioperl-core_server.py @@ -0,0 +1,295 @@ +import subprocess +import tempfile +import os +from pathlib import Path +from typing import List, Optional, Dict, Any + +# Helper function to run subprocess and handle errors +def _run_command(cmd: List[str]) -> Dict[str, Any]: + """ + Helper function to execute a command and capture its output. + """ + stdout_data = "" + stderr_data = "" + output_files: List[str] = [] + + try: + process = subprocess.run( + cmd, + capture_output=True, + text=True, + check=True, + ) + stdout_data = process.stdout + stderr_data = process.stderr + except FileNotFoundError as e: + # This typically means the command itself was not found in PATH + return { + "command_executed": " ".join(cmd), + "stdout": "", + "stderr": f"Command not found: {cmd[0]}. Please ensure BioPerl scripts are in your system's PATH. Error: {e}", + "error": "CommandNotFoundError", + "output_files": [], + } + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(cmd), + "stdout": e.stdout, + "stderr": e.stderr, + "error": f"Command failed with exit code {e.returncode}", + "output_files": [], + } + except Exception as e: + return { + "command_executed": " ".join(cmd), + "stdout": "", + "stderr": str(e), + "error": "Unexpected error", + "output_files": [], + } + + return { + "command_executed": " ".join(cmd), + "stdout": stdout_data, + "stderr": stderr_data, + "output_files": output_files, + } + + +@mcp.tool() +def bp_seqfeature_dump( + input_file: Path, + output_format: str = "GFF", + feature_types: Optional[List[str]] = None, + feature_sources: Optional[List[str]] = None, + feature_ids: Optional[List[str]] = None, + feature_tags: Optional[List[str]] = None, + output_file: Optional[Path] = None, + verbose: bool = False, +) -> Dict[str, Any]: + """ + Dumps sequence features from a BioPerl-supported input file (e.g., GenBank, EMBL) + into a specified output format. This tool wraps the `bp_seqfeature_dump.pl` script. + + Args: + input_file: Path to the input sequence file (e.g., GenBank, EMBL). + output_format: Output format for features (e.g., GFF, EMBL, GenBank, Fasta, Raw). Default: GFF. + feature_types: List of feature types to dump (e.g., CDS, gene, mRNA). Can be specified multiple times. + feature_sources: List of feature sources to dump. Can be specified multiple times. + feature_ids: List of feature IDs to dump. Can be specified multiple times. + feature_tags: List of feature tags to dump. Can be specified multiple times. + output_file: Path to the output file. If not specified, output goes to stdout. + verbose: Enable verbose output. + + Returns: + A dictionary containing command execution details, stdout, stderr, and output files. + """ + if not input_file.exists(): + raise FileNotFoundError(f"Input file not found: {input_file}") + if not input_file.is_file(): + raise ValueError(f"Input path is not a file: {input_file}") + + valid_formats = ["GFF", "EMBL", "GenBank", "Fasta", "Raw"] + if output_format not in valid_formats: + raise ValueError(f"Invalid output format: {output_format}. Must be one of {valid_formats}") + + cmd = ["bp_seqfeature_dump.pl", str(input_file)] + + cmd.extend(["--format", output_format]) + + if feature_types: + for ftype in feature_types: + cmd.extend(["--type", ftype]) + if feature_sources: + for source in feature_sources: + cmd.extend(["--source", source]) + if feature_ids: + for fid in feature_ids: + cmd.extend(["--id", fid]) + if feature_tags: + for tag in feature_tags: + cmd.extend(["--tag", tag]) + + if verbose: + cmd.append("--verbose") + + temp_output_path: Optional[Path] = None + if output_file: + # Ensure parent directory exists + output_file.parent.mkdir(parents=True, exist_ok=True) + cmd.extend(["--outfile", str(output_file)]) + else: + # If no output_file is specified, we'll capture stdout to a temp file + with tempfile.NamedTemporaryFile(delete=False, mode='w', encoding='utf-8') as tmp: + temp_output_path = Path(tmp.name) + cmd.extend(["--outfile", str(temp_output_path)]) + + result = _run_command(cmd) + + if result.get("error"): + # Clean up temp file if an error occurred before it was processed + if temp_output_path and temp_output_path.exists(): + os.remove(temp_output_path) + return result + + output_files_list: List[str] = [] + if output_file and output_file.exists(): + output_files_list.append(str(output_file)) + elif temp_output_path and temp_output_path.exists(): + # Read content from temp file and add to stdout, then delete temp file + with open(temp_output_path, 'r', encoding='utf-8') as f: + result["stdout"] = f.read() + os.remove(temp_output_path) + + result["output_files"] = output_files_list + return result + + +@mcp.tool() +def bp_genbank2gff3( + input_genbank_file: Path, + output_file: Optional[Path] = None, + verbose: bool = False, +) -> Dict[str, Any]: + """ + Converts a GenBank format file into GFF3 format. This tool wraps the `bp_genbank2gff3.pl` script. + + Args: + input_genbank_file: Path to the input GenBank file. + output_file: Path to the output GFF3 file. If not specified, output goes to stdout. + verbose: Enable verbose output. + + Returns: + A dictionary containing command execution details, stdout, stderr, and output files. + """ + if not input_genbank_file.exists(): + raise FileNotFoundError(f"Input GenBank file not found: {input_genbank_file}") + if not input_genbank_file.is_file(): + raise ValueError(f"Input path is not a file: {input_genbank_file}") + + cmd = ["bp_genbank2gff3.pl", str(input_genbank_file)] + + if verbose: + cmd.append("--verbose") + + temp_output_path: Optional[Path] = None + if output_file: + # Ensure parent directory exists + output_file.parent.mkdir(parents=True, exist_ok=True) + cmd.extend(["--outfile", str(output_file)]) + else: + with tempfile.NamedTemporaryFile(delete=False, mode='w', encoding='utf-8') as tmp: + temp_output_path = Path(tmp.name) + cmd.extend(["--outfile", str(temp_output_path)]) + + result = _run_command(cmd) + + if result.get("error"): + if temp_output_path and temp_output_path.exists(): + os.remove(temp_output_path) + return result + + output_files_list: List[str] = [] + if output_file and output_file.exists(): + output_files_list.append(str(output_file)) + elif temp_output_path and temp_output_path.exists(): + with open(temp_output_path, 'r', encoding='utf-8') as f: + result["stdout"] = f.read() + os.remove(temp_output_path) + + result["output_files"] = output_files_list + return result + + +@mcp.tool() +def bp_sreformat( + output_format: str, + input_file: Optional[Path] = None, + output_file: Optional[Path] = None, + id_type: Optional[str] = None, + width: int = 60, + uppercase: bool = False, + lowercase: bool = False, + gap_char: str = "-", +) -> Dict[str, Any]: + """ + Reformats sequence files between various BioPerl-supported formats. + This tool wraps the `bp_sreformat.pl` script. + + Args: + output_format: The desired output format (e.g., fasta, embl, genbank, phylip, nexus, raw). Required. + input_file: Path to the input sequence file. If not specified, reads from stdin (not directly supported by this wrapper, + `--input` is always used if `input_file` is provided). + output_file: Path to the output file. If not specified, output goes to stdout. + id_type: How to generate sequence IDs (e.g., accession, description, primary_id). + width: Line width for sequence output (e.g., fasta). Default: 60. + uppercase: Convert sequences to uppercase. + lowercase: Convert sequences to lowercase. + gap_char: Character to use for gaps. Default: '-'. + + Returns: + A dictionary containing command execution details, stdout, stderr, and output files. + """ + valid_formats = ["fasta", "embl", "genbank", "phylip", "nexus", "raw"] + if output_format not in valid_formats: + raise ValueError(f"Invalid output format: {output_format}. Must be one of {valid_formats}") + + if uppercase and lowercase: + raise ValueError("Cannot specify both 'uppercase' and 'lowercase'. Choose one or neither.") + + if width < 1: + raise ValueError("Width must be a positive integer.") + + cmd = ["bp_sreformat.pl"] + + cmd.extend(["--format", output_format]) + + if input_file: + if not input_file.exists(): + raise FileNotFoundError(f"Input file not found: {input_file}") + if not input_file.is_file(): + raise ValueError(f"Input path is not a file: {input_file}") + cmd.extend(["--input", str(input_file)]) + + if id_type: + valid_id_types = ["accession", "description", "primary_id"] + if id_type not in valid_id_types: + raise ValueError(f"Invalid ID type: {id_type}. Must be one of {valid_id_types}") + cmd.extend(["--id", id_type]) + + cmd.extend(["--width", str(width)]) + + if uppercase: + cmd.append("--uppercase") + if lowercase: + cmd.append("--lowercase") + + cmd.extend(["--gap", gap_char]) + + temp_output_path: Optional[Path] = None + if output_file: + # Ensure parent directory exists + output_file.parent.mkdir(parents=True, exist_ok=True) + cmd.extend(["--outfile", str(output_file)]) + else: + with tempfile.NamedTemporaryFile(delete=False, mode='w', encoding='utf-8') as tmp: + temp_output_path = Path(tmp.name) + cmd.extend(["--outfile", str(temp_output_path)]) + + result = _run_command(cmd) + + if result.get("error"): + if temp_output_path and temp_output_path.exists(): + os.remove(temp_output_path) + return result + + output_files_list: List[str] = [] + if output_file and output_file.exists(): + output_files_list.append(str(output_file)) + elif temp_output_path and temp_output_path.exists(): + with open(temp_output_path, 'r', encoding='utf-8') as f: + result["stdout"] = f.read() + os.remove(temp_output_path) + + result["output_files"] = output_files_list + return result \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-bioperl-core/app/perl-bioperl-core_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-bioperl-core/app/perl-bioperl-core_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..8d93bd24bc12011703282dda20d7bad5ed084f37 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-bioperl-core/app/perl-bioperl-core_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-bioperl-core/app/perl-bioperl-core_server.py') +SERVER_NAME = 'biosci_perl_bioperl_core' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-bioperl-core/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-bioperl-core/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-bioperl-core/app/requirements.txt @@ -0,0 +1 @@ + diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-bioperl-core/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-bioperl-core/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..a4eaa7a7e251316e6421b5ba61a97410ae50248a --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-bioperl-core/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-perl-bioperl-core: + build: . + image: mcp-perl-bioperl-core:latest + container_name: mcp-perl-bioperl-core + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=perl-bioperl-core + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-bioperl-core/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-bioperl-core/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..d632416e05f7b35d097d00e3281e1b396689a7f6 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-bioperl-core/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - perl-bioperl-core + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-bioperl-core/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-bioperl-core/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-bioperl-core/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-business-isbn-data/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-business-isbn-data/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..45e9ae622d589b3c90e57baf8a731ba77a37d08c --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-business-isbn-data/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install perl-business-isbn-data via conda (e.g., from bioconda) +RUN conda install -c bioconda perl-business-isbn-data -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY app/perl-business-isbn-data_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/perl-business-isbn-data_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/perl-business-isbn-data_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-business-isbn-data/app/perl-business-isbn-data_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-business-isbn-data/app/perl-business-isbn-data_server.py new file mode 100644 index 0000000000000000000000000000000000000000..2303865aa63e5eaf9469400f9b11354233206022 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-business-isbn-data/app/perl-business-isbn-data_server.py @@ -0,0 +1,265 @@ +import subprocess +from typing import Optional, List, Dict, Any +from pathlib import Path + +@mcp.tool() +def get_isbn_data_version() -> Dict[str, Any]: + """ + Retrieves the version of the installed Business::ISBN::Data pack. + This version indicates the currency of the ISBN range data. + """ + command = ["perl", "-MBusiness::ISBN::Data", "-e", "print $Business::ISBN::Data::VERSION"] + + try: + result = subprocess.run(command, capture_output=True, text=True, check=True) + return { + "command_executed": " ".join(command), + "version": result.stdout.strip(), + "stdout": result.stdout, + "stderr": result.stderr + } + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(command), + "error": str(e), + "stdout": e.stdout, + "stderr": e.stderr + } + +@mcp.tool() +def list_isbn_groups() -> Dict[str, Any]: + """ + Lists all ISBN group identifiers (countries, regions, or language areas) + supported by the current data pack. + """ + # Use Perl to extract keys from the %CountryData hash + perl_code = "use Business::ISBN::Data; print join(',', sort keys %Business::ISBN::Data::CountryData)" + command = ["perl", "-e", perl_code] + + try: + result = subprocess.run(command, capture_output=True, text=True, check=True) + groups = result.stdout.strip().split(",") if result.stdout.strip() else [] + return { + "command_executed": " ".join(command), + "total_groups": len(groups), + "groups": groups, + "stdout": result.stdout, + "stderr": result.stderr + } + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(command), + "error": str(e), + "stdout": e.stdout, + "stderr": e.stderr + } + +@mcp.tool() +def get_isbn_group_ranges(group_id: str) -> Dict[str, Any]: + """ + Retrieves the valid publisher ranges for a specific ISBN group. + + Args: + group_id: The group identifier (e.g., '0' for English, '3' for German, '978-609' for Armenia). + """ + if not group_id: + return {"error": "group_id parameter is required"} + + # Perl script to extract range data for a specific group + # Returns data in format: start:end:length + perl_code = ( + "use Business::ISBN::Data; " + f"my $data = $Business::ISBN::Data::CountryData{{'{group_id}'}}; " + "if ($data) { foreach my $r (@$data) { print join(':', @$r) . '\\n'; } }" + ) + command = ["perl", "-e", perl_code] + + try: + result = subprocess.run(command, capture_output=True, text=True, check=True) + lines = result.stdout.strip().split("\n") + ranges = [] + for line in lines: + if line: + parts = line.split(":") + if len(parts) >= 3: + ranges.append({ + "start": parts[0], + "end": parts[1], + "length": int(parts[2]) + }) + + return { + "command_executed": " ".join(command), + "group_id": group_id, + "ranges": ranges, + "stdout": result.stdout, + "stderr": result.stderr + } + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(command), + "error": str(e), + "stdout": e.stdout, + "stderr": e.stderr + } + +@mcp.tool() +def validate_isbn_prefix(isbn_prefix: str) -> Dict[str, Any]: + """ + Checks if a given ISBN prefix (e.g., '978-0-596') is valid according to the + ranges defined in the data pack. + + Args: + isbn_prefix: The ISBN prefix to validate (can include hyphens). + """ + # Clean the prefix + clean_prefix = isbn_prefix.replace("-", "") + + # This logic simulates how Business::ISBN uses the data pack to validate segments + perl_code = ( + "use Business::ISBN::Data; " + f"my $input = '{clean_prefix}'; " + "my $found = 0; " + "foreach my $group (keys %Business::ISBN::Data::CountryData) { " + " my $clean_group = $group; $clean_group =~ s/-//g; " + " if ($input =~ /^$clean_group/) { " + " my $rest = substr($input, length($clean_group)); " + " foreach my $range (@{$Business::ISBN::Data::CountryData{$group}}) { " + " my $sub = substr($rest, 0, $range->[2]); " + " if ($sub >= $range->[0] && $sub <= $range->[1]) { " + " print \"MATCH:group=$group:range=$range->[0]-$range->[1]\"; " + " $found = 1; last; " + " } " + " } " + " } " + " last if $found; " + "} " + "print 'NO_MATCH' unless $found;" + ) + + command = ["perl", "-e", perl_code] + + try: + result = subprocess.run(command, capture_output=True, text=True, check=True) + output = result.stdout.strip() + is_valid = output.startswith("MATCH") + + return { + "command_executed": " ".join(command), + "input_prefix": isbn_prefix, + "is_valid_in_data_pack": is_valid, + "match_details": output if is_valid else None, + "stdout": result.stdout, + "stderr": result.stderr + } + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(command), + "error": str(e), + "stdout": e.stdout, + "stderr": e.stderr + } + +@mcp.tool() +def check_isbn_data_integrity() -> Dict[str, Any]: + """ + Performs an internal integrity check on the loaded ISBN data pack to ensure + the data structure is valid and accessible. + """ + perl_code = ( + "use Business::ISBN::Data; " + "my $count = scalar keys %Business::ISBN::Data::CountryData; " + "print \"Groups loaded: $count\\n\"; " + "if ($count > 0) { print \"Status: OK\"; } else { print \"Status: ERROR - No data loaded\"; }" + ) + command = ["perl", "-e", perl_code] + + try: + result = subprocess.run(command, capture_output=True, text=True, check=True) + status = "OK" if "Status: OK" in result.stdout else "FAILED" + + return { + "command_executed": " ".join(command), + "integrity_status": status, + "stdout": result.stdout, + "stderr": result.stderr + } + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(command), + "error": str(e), + "stdout": e.stdout, + "stderr": e.stderr + } + +@mcp.tool() +def export_isbn_data_to_json(output_file: str) -> Dict[str, Any]: + """ + Exports the entire ISBN range data pack to a JSON file for external use. + + Args: + output_file: Path where the JSON data should be saved. + """ + output_path = Path(output_file) + + # Use JSON::PP which is core in modern Perl + perl_code = ( + "use Business::ISBN::Data; " + "use JSON::PP; " + "print encode_json(\\%Business::ISBN::Data::CountryData);" + ) + command = ["perl", "-e", perl_code] + + try: + result = subprocess.run(command, capture_output=True, text=True, check=True) + + with open(output_path, "w") as f: + f.write(result.stdout) + + return { + "command_executed": " ".join(command), + "output_file": str(output_path.absolute()), + "file_size_bytes": output_path.stat().st_size, + "stdout": "Data exported successfully", + "stderr": result.stderr + } + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(command), + "error": str(e), + "stdout": e.stdout, + "stderr": e.stderr + } + except Exception as e: + return { + "error": f"Failed to write file: {str(e)}" + } + +@mcp.tool() +def rebuild_isbn_data_from_xml(xml_source: str, output_module_path: Optional[str] = None) -> Dict[str, Any]: + """ + Internal utility to rebuild the Perl data module from an official ISBN Agency XML file. + Corresponds to the 'util/make_data.pl' functionality. + + Args: + xml_source: Path to the RangeMessage.xml file from the ISBN agency. + output_module_path: Optional path to save the generated .pm file. + """ + xml_path = Path(xml_source) + if not xml_path.exists(): + return {"error": f"XML source file not found: {xml_source}"} + + # This simulates the logic found in the package's util/make_data.pl + # It requires XML::LibXML or similar, which might not be in the base env, + # so we check for the script or implement a basic version. + + # For this MCP tool, we will attempt to run the logic via perl if the environment allows. + # Note: This is an advanced maintenance tool. + + command = ["perl", "-MBusiness::ISBN::Data", "-e", "print 'Rebuild logic requires util/make_data.pl from source'"] + + return { + "command_executed": " ".join(command), + "info": "This tool typically requires the source distribution's util/ directory. Use the data pack as provided by the package manager for standard use.", + "xml_provided": str(xml_path.absolute()) + } \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-business-isbn-data/app/perl-business-isbn-data_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-business-isbn-data/app/perl-business-isbn-data_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..df706b46af2b451c70fe6e59e8772bc97f9a8810 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-business-isbn-data/app/perl-business-isbn-data_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-business-isbn-data/app/perl-business-isbn-data_server.py') +SERVER_NAME = 'biosci_perl_business_isbn_data' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-business-isbn-data/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-business-isbn-data/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-business-isbn-data/app/requirements.txt @@ -0,0 +1 @@ + diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-business-isbn-data/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-business-isbn-data/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..3ae6a649cd0c30a94e425e20c628f855f27d3476 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-business-isbn-data/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-perl-business-isbn-data: + build: . + image: mcp-perl-business-isbn-data:latest + container_name: mcp-perl-business-isbn-data + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=perl-business-isbn-data + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-business-isbn-data/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-business-isbn-data/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..208fe07a766682b2cceafea20c40fe1204e961ca --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-business-isbn-data/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - perl-business-isbn-data + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-business-isbn-data/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-business-isbn-data/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-business-isbn-data/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-class-inspector/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-class-inspector/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..731adda7c78769ef471d0507666960154c1cfca5 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-class-inspector/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install perl-class-inspector via conda (e.g., from bioconda) +RUN conda install -c bioconda perl-class-inspector -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY app/perl-class-inspector_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/perl-class-inspector_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/perl-class-inspector_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-class-inspector/app/perl-class-inspector_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-class-inspector/app/perl-class-inspector_server.py new file mode 100644 index 0000000000000000000000000000000000000000..375b8090d322047a06d4763c6c38c6c40fbf717e --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-class-inspector/app/perl-class-inspector_server.py @@ -0,0 +1,118 @@ +import subprocess +import tempfile +from pathlib import Path +from typing import Dict, Optional, List +import shlex + +# Note: The 'mcp' import is not needed as per the instructions. +# This is a placeholder for the actual decorator. +class Mcp: + def tool(self, *args, **kwargs): + def decorator(f): + return f + return decorator + +mcp = Mcp() + +@mcp.tool() +def inspect_class( + class_name: str +) -> Dict[str, any]: + """ + Uses the Perl Class::Inspector module to get information about a Perl class. + + This tool dynamically creates and executes a Perl script to inspect the specified class, + reporting its file path and public methods. It requires that Perl and the + Class::Inspector module are available in the execution environment. + + Args: + class_name: The name of the Perl class to inspect (e.g., 'Data::Dumper', 'Bio::SeqIO'). + + Returns: + A dictionary containing the command executed, stdout with the inspection results, + stderr, and a list of output files (which is always empty). + """ + if not class_name or not isinstance(class_name, str): + raise ValueError("class_name must be a non-empty string.") + + # A robust Perl script that uses Class::Inspector to get class details. + # It includes error handling for missing modules or classes. + perl_script_content = f""" + use strict; + use warnings; + + # Try to load the module, exit gracefully if not found. + eval "use Class::Inspector;"; + if ($@) {{ + print STDERR "Error: The 'Class::Inspector' Perl module is not installed in the environment.\\n"; + exit 1; + }} + + my $class_to_inspect = '{class_name}'; + + # Check if the class is installed/loadable + unless (Class::Inspector->installed($class_to_inspect)) {{ + print STDERR "Error: Class '$class_to_inspect' is not installed or could not be loaded.\\n"; + exit 1; + }} + + # Get information + my $filename = Class::Inspector->filename($class_to_inspect) || 'N/A'; + my @methods = Class::Inspector->methods($class_to_inspect, 'public'); + + # Print information in a structured, human-readable format + print "Class: $class_to_inspect\\n"; + print "File: $filename\\n"; + print "Public Methods:\\n"; + if (@methods) {{ + foreach my $method (sort @methods) {{ + print " - $method\\n"; + }} + }} else {{ + print " (No public methods found)\\n"; + }} + """ + + script_path = None + try: + # Use a temporary file for the Perl script to avoid command line length issues + # and to handle complex scripts cleanly. + with tempfile.NamedTemporaryFile(mode='w', suffix='.pl', delete=False) as tmp_script: + script_path = Path(tmp_script.name) + tmp_script.write(perl_script_content) + + command = ['perl', str(script_path)] + command_str = shlex.join(command) + + result = subprocess.run( + command, + capture_output=True, + text=True, + check=False # The script has internal error handling, so we check returncode manually. + ) + + return { + "command_executed": command_str, + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": [] + } + except FileNotFoundError: + # This error occurs if 'perl' is not in the system's PATH. + return { + "command_executed": f"perl {class_name}", + "stdout": "", + "stderr": "Error: 'perl' command not found. Is Perl installed and in the system's PATH?", + "output_files": [] + } + except Exception as e: + return { + "command_executed": f"perl {class_name}", + "stdout": "", + "stderr": f"An unexpected error occurred: {str(e)}", + "output_files": [] + } + finally: + # Ensure the temporary script file is always cleaned up. + if script_path and script_path.exists(): + script_path.unlink() diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-class-inspector/app/perl-class-inspector_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-class-inspector/app/perl-class-inspector_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..7450f0900c268cf13ba5acb535e8786992faa863 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-class-inspector/app/perl-class-inspector_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-class-inspector/app/perl-class-inspector_server.py') +SERVER_NAME = 'biosci_perl_class_inspector' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-class-inspector/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-class-inspector/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-class-inspector/app/requirements.txt @@ -0,0 +1 @@ + diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-class-inspector/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-class-inspector/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..c6cc102c27062cc1dc8058b812a7e244bff80bd2 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-class-inspector/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-perl-class-inspector: + build: . + image: mcp-perl-class-inspector:latest + container_name: mcp-perl-class-inspector + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=perl-class-inspector + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-class-inspector/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-class-inspector/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..0f1437910a6a0d25770e5a801af4459da5effeaf --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-class-inspector/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - perl-class-inspector + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-class-inspector/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-class-inspector/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-class-inspector/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-constant/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-constant/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..fca2cbe684d0a861ce72b4d3f5eff7b7c8d678ec --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-constant/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install perl-constant via conda (e.g., from bioconda) +RUN conda install -c bioconda perl-constant -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY app/perl-constant_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/perl-constant_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/perl-constant_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-constant/app/perl-constant_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-constant/app/perl-constant_server.py new file mode 100644 index 0000000000000000000000000000000000000000..ed1848ca876216e1fd767d598bf9f0f68c3081a0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-constant/app/perl-constant_server.py @@ -0,0 +1,189 @@ +import subprocess +import tempfile +from pathlib import Path +from typing import List, Optional, Union + +# No need to import mcp, as per instructions. + +@mcp.tool() +def run_perl( + program_file: Optional[Path] = None, + program_arguments: List[str] = [], + program_string: Optional[str] = None, + program_string_extended: Optional[str] = None, + record_separator: Optional[str] = None, + autosplit_mode: bool = False, + unicode_features: Optional[str] = None, + check_syntax_only: bool = False, + debugger: Optional[str] = None, + debugging_flags: Optional[str] = None, + no_sitecustomize: bool = False, + split_pattern: Optional[str] = None, + edit_in_place_extension: Optional[str] = None, + include_directories: List[Path] = [], + line_ending_terminator: Optional[str] = None, + use_modules: List[str] = [], + no_modules: List[str] = [], + loop_around_program: bool = False, + loop_and_print: bool = False, + rudimentary_switch_parsing: bool = False, + search_path_for_program: bool = False, + tainting_warnings: bool = False, + tainting_checks: bool = False, + dump_core_after_parsing: bool = False, + allow_unsafe_operations: bool = False, + print_version: bool = False, + print_config_summary_variable: Optional[str] = None, + enable_warnings: bool = False, + enable_all_warnings: bool = False, + ignore_text_before_shebang_dir: Optional[Path] = None, + disable_all_warnings: bool = False, +) -> dict: + """ + Executes the Perl interpreter with specified options and a program. + + This tool provides access to the core Perl interpreter's command-line switches, + allowing users to run Perl scripts or one-liners with various configurations + for debugging, warnings, file processing, and module loading. + """ + command = ["perl"] + output_files = [] + + # Input validation + if not program_file and not program_string and not program_string_extended and not print_version and print_config_summary_variable is None: + raise ValueError( + "Either 'program_file', 'program_string', 'program_string_extended', " + "'print_version', or 'print_config_summary_variable' must be provided " + "to specify what Perl should execute or report." + ) + + if program_file and (program_string or program_string_extended): + raise ValueError("Cannot specify both 'program_file' and a program string ('program_string' or 'program_string_extended').") + + if program_string and program_string_extended: + raise ValueError("Cannot specify both 'program_string' (-e) and 'program_string_extended' (-E).") + + if autosplit_mode and not (loop_around_program or loop_and_print): + raise ValueError("'-a' (autosplit_mode) requires '-n' (loop_around_program) or '-p' (loop_and_print).") + + if split_pattern and not autosplit_mode: + raise ValueError("'-F' (split_pattern) requires '-a' (autosplit_mode).") + + if loop_around_program and loop_and_print: + raise ValueError("Cannot specify both '-n' (loop_around_program) and '-p' (loop_and_print).") + + if disable_all_warnings and (enable_warnings or enable_all_warnings): + raise ValueError("'-X' (disable_all_warnings) cannot be used with '-w' (enable_warnings) or '-W' (enable_all_warnings).") + + # Build the command + if record_separator is not None: + command.append(f"-0{record_separator}") + if autosplit_mode: + command.append("-a") + if unicode_features is not None: + command.append(f"-C{unicode_features}") + if check_syntax_only: + command.append("-c") + if debugger is not None: + # -d:debugger or -d (if debugger is empty string) + command.append(f"-d:{debugger}") + if debugging_flags is not None: + command.append(f"-D{debugging_flags}") + if program_string: + command.extend(["-e", program_string]) + if program_string_extended: + command.extend(["-E", program_string_extended]) + if no_sitecustomize: + command.append("-f") + if split_pattern is not None: + command.append(f"-F{split_pattern}") + if edit_in_place_extension is not None: + # -i[extension] or -i (if extension is empty string) + command.append(f"-i{edit_in_place_extension}") + for inc_dir in include_directories: + if not inc_dir.is_dir(): + raise ValueError(f"Include directory '{inc_dir}' does not exist or is not a directory.") + command.extend(["-I", str(inc_dir)]) + if line_ending_terminator is not None: + # -l[octal] or -l (if terminator is empty string) + command.append(f"-l{line_ending_terminator}") + for module_name in use_modules: + command.extend(["-M", module_name]) + for module_name in no_modules: + command.extend(["-M-", module_name]) # -M-module for 'no module' + if loop_around_program: + command.append("-n") + if loop_and_print: + command.append("-p") + if rudimentary_switch_parsing: + command.append("-s") + if search_path_for_program: + command.append("-S") + if tainting_warnings: + command.append("-t") + if tainting_checks: + command.append("-T") + if dump_core_after_parsing: + command.append("-u") + if allow_unsafe_operations: + command.append("-U") + if print_version: + command.append("-v") + if print_config_summary_variable is not None: + # -V[:variable] or -V (if variable is empty string) + if print_config_summary_variable == "": + command.append("-V") + else: + command.append(f"-V:{print_config_summary_variable}") + if enable_warnings: + command.append("-w") + if enable_all_warnings: + command.append("-W") + if ignore_text_before_shebang_dir is not None: + # -x[directory] or -x (if directory is empty string) + if ignore_text_before_shebang_dir and not ignore_text_before_shebang_dir.is_dir(): + raise ValueError(f"Directory for '-x' '{ignore_text_before_shebang_dir}' does not exist or is not a directory.") + command.append(f"-x{ignore_text_before_shebang_dir}") + if disable_all_warnings: + command.append("-X") + + # Program file and arguments + if program_file: + if not program_file.is_file(): + raise FileNotFoundError(f"Program file '{program_file}' not found.") + command.append(str(program_file)) + + if program_arguments: + command.extend(program_arguments) + + try: + process = subprocess.run( + command, + capture_output=True, + text=True, + check=True + ) + stdout = process.stdout + stderr = process.stderr + return { + "command_executed": " ".join(command), + "stdout": stdout, + "stderr": stderr, + "output_files": output_files, + } + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(command), + "stdout": e.stdout, + "stderr": e.stderr, + "output_files": output_files, + "error": f"Perl execution failed with exit code {e.returncode}", + } + except FileNotFoundError: + return { + "command_executed": " ".join(command), + "stdout": "", + "stderr": "Error: 'perl' command not found. Is Perl installed and in your PATH?", + "output_files": output_files, + "error": "Perl executable not found.", + } \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-constant/app/perl-constant_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-constant/app/perl-constant_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..185113443fceeb632082e517583413ddd20d8c79 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-constant/app/perl-constant_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-constant/app/perl-constant_server.py') +SERVER_NAME = 'biosci_perl_constant' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-constant/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-constant/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-constant/app/requirements.txt @@ -0,0 +1 @@ + diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-constant/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-constant/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..7d75680c8fd563b60bfe4a0bad9dc67b2b942ce1 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-constant/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-perl-constant: + build: . + image: mcp-perl-constant:latest + container_name: mcp-perl-constant + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=perl-constant + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-constant/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-constant/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..a7ee160d72e2ebc00f01f3c82414a5bfdc8e49bd --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-constant/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - perl-constant + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-constant/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-constant/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-constant/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-cpan-meta/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-cpan-meta/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-cpan-meta/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-digest-md5/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-digest-md5/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..d83f1a56fe274c3bab7e8aff30d31347b267f00a --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-digest-md5/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-perl-digest-md5: + build: . + image: mcp-perl-digest-md5:latest + container_name: mcp-perl-digest-md5 + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=perl-digest-md5 + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-digest-md5/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-digest-md5/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..9a6c0a5fcd638cb87c226c42be1bb3642097b705 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-digest-md5/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - perl-digest-md5 + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-encode-locale/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-encode-locale/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..3b3d194dcff41dcb32832b9006eff5cb6b75971f --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-encode-locale/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install perl-encode-locale via conda (e.g., from bioconda) +RUN conda install -c bioconda perl-encode-locale -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY app/perl-encode-locale_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/perl-encode-locale_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/perl-encode-locale_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-encode-locale/app/perl-encode-locale_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-encode-locale/app/perl-encode-locale_server.py new file mode 100644 index 0000000000000000000000000000000000000000..cd1e61c4a7b90d33a4e2735fc1738a8a6459aafa --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-encode-locale/app/perl-encode-locale_server.py @@ -0,0 +1,74 @@ +import subprocess +import logging +from typing import Optional, List, Dict, Any +from pathlib import Path + +# Configure logging +logging.basicConfig(level=logging.INFO) +logger = logging.getLogger(__name__) + +# Mock mcp decorator for standalone execution +class mcp: + @staticmethod + def tool(func): + def wrapper(*args, **kwargs): + return func(*args, **kwargs) + return wrapper + +@mcp.tool +def perl_encode_locale() -> Dict[str, Any]: + """ + Determines and returns the current system's locale encoding using the Perl Encode::Locale module. + + This tool executes a Perl one-liner that loads the Encode::Locale module and + prints the detected locale encoding to standard output. It is a simple utility + to inspect the execution environment's character encoding settings. + + Returns: + Dict[str, Any]: A dictionary containing the command executed, stdout, stderr, + and the detected locale encoding. + """ + command = [ + "perl", + "-MEncode::Locale", + "-e", + "print $Encode::Locale::ENCODING_LOCALE", + ] + + command_str = " ".join(command) + logger.info(f"Executing command: {command_str}") + + try: + result = subprocess.run( + command, + capture_output=True, + text=True, + check=True, + ) + + stdout_cleaned = result.stdout.strip() + + return { + "command_executed": command_str, + "stdout": result.stdout, + "stderr": result.stderr, + "locale_encoding": stdout_cleaned, + } + except FileNotFoundError: + logger.error("Perl not found. Please ensure Perl is installed and in your PATH.") + return { + "command_executed": command_str, + "stdout": "", + "stderr": "Perl not found. Please ensure Perl is installed and in your PATH.", + "error": "FileNotFoundError", + } + except subprocess.CalledProcessError as e: + logger.error(f"Command failed with exit code {e.returncode}") + logger.error(f"Stderr: {e.stderr}") + return { + "command_executed": command_str, + "stdout": e.stdout, + "stderr": e.stderr, + "error": "CalledProcessError", + "return_code": e.returncode, + } diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-encode-locale/app/perl-encode-locale_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-encode-locale/app/perl-encode-locale_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..e7262a1fafbc00aa6b03ca0f498d8f266c0f83f7 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-encode-locale/app/perl-encode-locale_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-encode-locale/app/perl-encode-locale_server.py') +SERVER_NAME = 'biosci_perl_encode_locale' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-encode-locale/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-encode-locale/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-encode-locale/app/requirements.txt @@ -0,0 +1 @@ + diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-encode-locale/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-encode-locale/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..b95551adf433024f7e40ed5d637db0caf2b97a43 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-encode-locale/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-perl-encode-locale: + build: . + image: mcp-perl-encode-locale:latest + container_name: mcp-perl-encode-locale + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=perl-encode-locale + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-encode-locale/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-encode-locale/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..df4e470c2927d1224d1dd3821e33947247718e57 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-encode-locale/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - perl-encode-locale + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-encode-locale/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-encode-locale/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-encode-locale/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-error/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-error/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..481c1f968dfea29e8306b95fd767f2d2b815bc9f --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-error/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install perl-error via conda (e.g., from bioconda) +RUN conda install -c bioconda perl-error -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY app/perl-error_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/perl-error_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/perl-error_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-error/app/perl-error_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-error/app/perl-error_server.py new file mode 100644 index 0000000000000000000000000000000000000000..c27a4266a1fae9653038a0a8b6515133353d5c72 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-error/app/perl-error_server.py @@ -0,0 +1,159 @@ +import subprocess +from pathlib import Path +from typing import Optional, List, Dict + +# @mcp.tool() is a placeholder for the actual decorator. +# The final code should be used in an environment where mcp is defined. + +def tool_placeholder(func): + """A placeholder decorator for mcp.tool.""" + return func + +@tool_placeholder +def run_perl( + programfile: Optional[Path] = None, + arguments: Optional[List[str]] = None, + execute_program: Optional[List[str]] = None, + execute_program_extended: Optional[List[str]] = None, + check_syntax_only: bool = False, + record_separator: Optional[str] = None, + autosplit: bool = False, + split_pattern: Optional[str] = None, + loop_around_program: bool = False, + loop_and_print: bool = False, + in_place_edit_extension: Optional[str] = None, + include_directories: Optional[List[Path]] = None, + use_modules: Optional[List[str]] = None, + no_use_modules: Optional[List[str]] = None, + enable_switch_parsing: bool = False, + search_path_for_program: bool = False, + unicode_features: Optional[str] = None, + run_under_debugger: bool = False, + debugger_module: Optional[str] = None, + debugging_flags: Optional[str] = None, + no_sitecustomize: bool = False, + enable_line_ending_processing: bool = False, + line_terminator_octal: Optional[str] = None, + tainting_warnings: bool = False, + tainting_checks: bool = False, + dump_core: bool = False, + allow_unsafe_operations: bool = False, + print_version: bool = False, + print_configuration: bool = False, + config_variable: Optional[str] = None, + enable_warnings: bool = False, + enable_all_warnings: bool = False, + disable_all_warnings: bool = False, + ignore_text_before_shebang: bool = False, + change_directory: Optional[Path] = None, +) -> Dict[str, any]: + """ + Executes the Perl interpreter with specified switches and a program file or one-liners. + + This tool wraps the generic 'perl' command-line executable. The name 'perl-error' + refers to a Perl module for exception handling, not a standalone command-line tool. + This function provides the functionality of the Perl interpreter itself, which is + used to run scripts that may depend on modules like Error.pm. + """ + # --- Input Validation --- + if not any([programfile, execute_program, execute_program_extended, print_version, print_configuration]): + raise ValueError("A 'programfile', 'execute_program', 'execute_program_extended', 'print_version', or 'print_configuration' must be provided.") + + if programfile and (execute_program or execute_program_extended): + raise ValueError("'programfile' cannot be used with 'execute_program' or 'execute_program_extended'.") + + if programfile: + if not programfile.is_file(): + raise FileNotFoundError(f"Program file not found or is not a regular file: {programfile}") + + if debugger_module and not run_under_debugger: + raise ValueError("'debugger_module' can only be specified when 'run_under_debugger' is True.") + + if config_variable and not print_configuration: + raise ValueError("'config_variable' can only be specified when 'print_configuration' is True.") + + if line_terminator_octal and not enable_line_ending_processing: + raise ValueError("'line_terminator_octal' can only be specified when 'enable_line_ending_processing' is True.") + + if change_directory and not ignore_text_before_shebang: + raise ValueError("'change_directory' can only be specified when 'ignore_text_before_shebang' is True.") + + if include_directories: + for directory in include_directories: + if not directory.is_dir(): + raise NotADirectoryError(f"Include directory not found or not a directory: {directory}") + + if change_directory and not change_directory.is_dir(): + raise NotADirectoryError(f"Directory for '-x' option not found or not a directory: {change_directory}") + + # --- Command Construction --- + cmd = ["perl"] + + if record_separator is not None: cmd.append(f"-0{record_separator}") + if autosplit: cmd.append("-a") + if unicode_features is not None: cmd.append(f"-C{unicode_features}") + if check_syntax_only: cmd.append("-c") + if run_under_debugger: cmd.append(f"-d{':' + debugger_module if debugger_module else ''}") + if debugging_flags is not None: cmd.append(f"-D{debugging_flags}") + if execute_program: + for prog_line in execute_program: cmd.extend(["-e", prog_line]) + if execute_program_extended: + for prog_line in execute_program_extended: cmd.extend(["-E", prog_line]) + if no_sitecustomize: cmd.append("-f") + if split_pattern is not None: cmd.append(f"-F{split_pattern}") + if in_place_edit_extension is not None: cmd.append(f"-i{in_place_edit_extension}") + if include_directories: + for directory in include_directories: cmd.extend(["-I", str(directory)]) + if enable_line_ending_processing: cmd.append(f"-l{line_terminator_octal if line_terminator_octal else ''}") + if use_modules: + for module in use_modules: cmd.append(f"-M{module}") + if no_use_modules: + for module in no_use_modules: cmd.append(f"-m{module}") + if loop_around_program: cmd.append("-n") + if loop_and_print: cmd.append("-p") + if enable_switch_parsing: cmd.append("-s") + if search_path_for_program: cmd.append("-S") + if tainting_warnings: cmd.append("-t") + if tainting_checks: cmd.append("-T") + if dump_core: cmd.append("-u") + if allow_unsafe_operations: cmd.append("-U") + if print_version: cmd.append("-v") + if print_configuration: cmd.append(f"-V{':' + config_variable if config_variable else ''}") + if enable_warnings: cmd.append("-w") + if enable_all_warnings: cmd.append("-W") + if ignore_text_before_shebang: + if change_directory: cmd.extend(["-x", str(change_directory)]) + else: cmd.append("-x") + if disable_all_warnings: cmd.append("-X") + + if programfile: + cmd.append(str(programfile)) + if arguments: + cmd.extend(arguments) + + # --- Subprocess Execution --- + command_executed = " ".join(cmd) + try: + result = subprocess.run( + cmd, + capture_output=True, + text=True, + check=True + ) + return { + "command_executed": command_executed, + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": [] + } + except FileNotFoundError: + raise RuntimeError("Error: 'perl' command not found. Please ensure Perl is installed and in your system's PATH.") + except subprocess.CalledProcessError as e: + # Return a structured error, but do not raise an exception from the MCP tool itself + return { + "command_executed": command_executed, + "stdout": e.stdout, + "stderr": e.stderr, + "output_files": [], + "error": f"Perl process exited with non-zero status {e.returncode}." + } diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-error/app/perl-error_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-error/app/perl-error_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..e5546ab94c4d9501913b62a37b1167aff4c502a8 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-error/app/perl-error_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-error/app/perl-error_server.py') +SERVER_NAME = 'biosci_perl_error' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-error/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-error/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-error/app/requirements.txt @@ -0,0 +1 @@ + diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-error/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-error/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..99a39ebeec8a6f12432cf03f3234728164c0ef21 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-error/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-perl-error: + build: . + image: mcp-perl-error:latest + container_name: mcp-perl-error + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=perl-error + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-error/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-error/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..2b5693a35a41c40e6518b54fda10a8bd1e9fb7ea --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-error/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - perl-error + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-error/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-error/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-error/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-exception-class/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-exception-class/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..6116c8502aa9651cd86b58054e1d3d7315f36279 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-exception-class/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install perl-exception-class via conda (e.g., from bioconda) +RUN conda install -c bioconda perl-exception-class -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY app/perl-exception-class_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/perl-exception-class_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/perl-exception-class_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-exception-class/app/perl-exception-class_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-exception-class/app/perl-exception-class_server.py new file mode 100644 index 0000000000000000000000000000000000000000..d4976f06083558122db43a3e2c2be2ded6b77286 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-exception-class/app/perl-exception-class_server.py @@ -0,0 +1,235 @@ +import subprocess +import tempfile +from pathlib import Path +from typing import List, Optional, Dict, Any + +# This is a placeholder for the MCP decorator. +# In a real MCP environment, this would be: +# from mcp import tool as mcp_tool +class mcp: + @staticmethod + def tool(func): + return func + +@mcp.tool +def run_perl( + programfile: Optional[Path] = None, + arguments: Optional[List[str]] = None, + program_lines: Optional[List[str]] = None, + program_lines_extended: Optional[List[str]] = None, + record_separator_octal: Optional[str] = None, + autosplit: bool = False, + unicode_features: Optional[str] = None, + check_syntax_only: bool = False, + debugger: Optional[str] = None, + debugging_flags: Optional[str] = None, + no_sitecustomize: bool = False, + split_pattern: Optional[str] = None, + in_place_edit: Optional[str] = None, + include_directories: Optional[List[Path]] = None, + line_ending_processing_octal: Optional[str] = None, + use_modules: Optional[List[str]] = None, + use_modules_no_imports: Optional[List[str]] = None, + no_modules: Optional[List[str]] = None, + no_modules_no_imports: Optional[List[str]] = None, + loop_around_program: bool = False, + loop_and_print: bool = False, + parse_switches_after_program: bool = False, + search_path_for_program: bool = False, + tainting_warnings: bool = False, + tainting_checks: bool = False, + dump_core: bool = False, + allow_unsafe_operations: bool = False, + print_version: bool = False, + print_config: Optional[str] = None, + enable_warnings: bool = False, + enable_all_warnings: bool = False, + ignore_preamble_dir: Optional[str] = None, + disable_all_warnings: bool = False, +) -> Dict[str, Any]: + """ + Executes the Perl interpreter. This tool is a wrapper for the `perl` command, + which is the execution environment for Perl modules like Exception-Class. + + Args: + programfile: Path to the Perl program file to execute. + arguments: List of arguments to pass to the program file. + program_lines: List of strings, each executed as a line of the program (-e). + program_lines_extended: Like program_lines, but enables all optional features (-E). + record_separator_octal: Specify record separator as an octal value (-0). + autosplit: Enable autosplit mode with -n or -p (splits $_ into @F) (-a). + unicode_features: Enable the listed Unicode features (-C). + check_syntax_only: Check syntax only; does not execute the program (-c). + debugger: Run the program under the specified debugger (-d[:debugger]). + debugging_flags: Set debugging flags (-D). + no_sitecustomize: Don't do $sitelib/sitecustomize.pl at startup (-f). + split_pattern: Specify the split() pattern for the -a switch (-F). + in_place_edit: Edit <> files in place. If a value is supplied, it is used as the backup extension (-i[extension]). + include_directories: List of directories to add to @INC (-I). + line_ending_processing_octal: Enable line ending processing and specify the line terminator (-l[octal]). + use_modules: Execute 'use module...' before the program (-Mmodule). + use_modules_no_imports: Execute 'use module ()' before the program (-mmodule). + no_modules: Execute 'no module...' before the program (-M-module). + no_modules_no_imports: Execute 'no module ()' before the program (-m-module). + loop_around_program: Assume a "while (<>) { ... }" loop around the program (-n). + loop_and_print: Assume a loop like -n but also print the line, like sed (-p). + parse_switches_after_program: Enable rudimentary parsing for switches after the program file (-s). + search_path_for_program: Look for the program file using the PATH environment variable (-S). + tainting_warnings: Enable tainting warnings (-t). + tainting_checks: Enable tainting checks (-T). + dump_core: Dump core after parsing the program (-u). + allow_unsafe_operations: Allow unsafe operations (-U). + print_version: Print version, patchlevel, and license, then exit (-v). + print_config: Print configuration summary or a single Config.pm variable, then exit (-V[:variable]). + enable_warnings: Enable many useful warnings (-w). + enable_all_warnings: Enable all warnings (-W). + ignore_preamble_dir: Ignore text before #!perl line. Optionally cd to the specified directory (-x[directory]). + disable_all_warnings: Disable all warnings (-X). + + Returns: + A dictionary containing the command executed, stdout, stderr, and a list of output files. + """ + # --- Input Validation --- + program_source_count = sum( + p is not None for p in [programfile, program_lines, program_lines_extended] + ) + if program_source_count > 1: + raise ValueError( + "Only one of 'programfile', 'program_lines', or 'program_lines_extended' can be specified." + ) + + is_info_command = print_version or (print_config is not None) + if program_source_count == 0 and not is_info_command: + raise ValueError( + "A program source ('programfile', 'program_lines', or 'program_lines_extended') must be provided." + ) + + if programfile and not programfile.is_file(): + raise FileNotFoundError(f"Program file not found: {programfile}") + + if include_directories: + for directory in include_directories: + if not directory.is_dir(): + raise NotADirectoryError(f"Include directory not found: {directory}") + + # --- Command Construction --- + cmd = ["perl"] + + if record_separator_octal is not None: + cmd.append(f"-0{record_separator_octal}") + if autosplit: + cmd.append("-a") + if unicode_features is not None: + cmd.append(f"-C{unicode_features}") + if check_syntax_only: + cmd.append("-c") + if debugger is not None: + cmd.append(f"-d:{debugger}" if debugger else "-d") + if debugging_flags is not None: + cmd.append(f"-D{debugging_flags}") + if no_sitecustomize: + cmd.append("-f") + if split_pattern is not None: + cmd.append(f"-F{split_pattern}") + if in_place_edit is not None: + cmd.append(f"-i{in_place_edit}") + if include_directories: + for directory in include_directories: + cmd.append(f"-I{directory}") + if line_ending_processing_octal is not None: + cmd.append(f"-l{line_ending_processing_octal}") + if use_modules: + for module in use_modules: + cmd.append(f"-M{module}") + if use_modules_no_imports: + for module in use_modules_no_imports: + cmd.append(f"-m{module}") + if no_modules: + for module in no_modules: + cmd.append(f"-M-{module}") + if no_modules_no_imports: + for module in no_modules_no_imports: + cmd.append(f"-m-{module}") + if loop_around_program: + cmd.append("-n") + if loop_and_print: + cmd.append("-p") + if parse_switches_after_program: + cmd.append("-s") + if search_path_for_program: + cmd.append("-S") + if tainting_warnings: + cmd.append("-t") + if tainting_checks: + cmd.append("-T") + if dump_core: + cmd.append("-u") + if allow_unsafe_operations: + cmd.append("-U") + if print_version: + cmd.append("-v") + if print_config is not None: + cmd.append(f"-V:{print_config}" if print_config else "-V") + if enable_warnings: + cmd.append("-w") + if enable_all_warnings: + cmd.append("-W") + if ignore_preamble_dir is not None: + cmd.append(f"-x{ignore_preamble_dir}") + if disable_all_warnings: + cmd.append("-X") + + # --- Program and Arguments --- + if program_lines: + for line in program_lines: + cmd.extend(["-e", line]) + if program_lines_extended: + for line in program_lines_extended: + cmd.extend(["-E", line]) + + if programfile: + cmd.append(str(programfile)) + + if arguments: + cmd.extend(arguments) + + # --- Subprocess Execution --- + command_executed = " ".join(cmd) + try: + result = subprocess.run( + cmd, + capture_output=True, + text=True, + check=True, + ) + stdout = result.stdout + stderr = result.stderr + except FileNotFoundError: + return { + "error": "Perl executable not found. Please ensure it is in your PATH.", + "command_executed": command_executed, + "stdout": "", + "stderr": "FileNotFoundError: 'perl' not found.", + "output_files": [] + } + except subprocess.CalledProcessError as e: + return { + "error": "Perl execution failed.", + "command_executed": command_executed, + "stdout": e.stdout, + "stderr": e.stderr, + "return_code": e.returncode, + "output_files": [] + } + + # --- Structured Result Return --- + output_files = [] + if programfile and in_place_edit is not None: + output_files.append(str(programfile)) + + return { + "command_executed": command_executed, + "stdout": stdout, + "stderr": stderr, + "output_files": output_files, + } \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-exception-class/app/perl-exception-class_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-exception-class/app/perl-exception-class_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..7ebc26f48ddd291cf0322bb4ed514d79bf0089a4 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-exception-class/app/perl-exception-class_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-exception-class/app/perl-exception-class_server.py') +SERVER_NAME = 'biosci_perl_exception_class' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-exception-class/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-exception-class/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-exception-class/app/requirements.txt @@ -0,0 +1 @@ + diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-exception-class/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-exception-class/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..26fc6034a589d9b309709752d48a2b94347e1314 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-exception-class/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-perl-exception-class: + build: . + image: mcp-perl-exception-class:latest + container_name: mcp-perl-exception-class + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=perl-exception-class + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-exception-class/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-exception-class/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..12401e3821580643f0a2302537548ee906f1b54e --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-exception-class/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - perl-exception-class + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-exception-class/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-exception-class/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-exception-class/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-extutils-makemaker/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-extutils-makemaker/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..9903a24227323d44ec06ba9f1bd33ccd62745a74 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-extutils-makemaker/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install perl-extutils-makemaker via conda (e.g., from bioconda) +RUN conda install -c bioconda perl-extutils-makemaker -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY app/perl-extutils-makemaker_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/perl-extutils-makemaker_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/perl-extutils-makemaker_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-extutils-makemaker/app/perl-extutils-makemaker_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-extutils-makemaker/app/perl-extutils-makemaker_server.py new file mode 100644 index 0000000000000000000000000000000000000000..588cce4d94fddfdcedad612e324a1c0c9d5d41b0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-extutils-makemaker/app/perl-extutils-makemaker_server.py @@ -0,0 +1,326 @@ +import subprocess +import logging +from pathlib import Path +from typing import Optional, List, Dict + +# The mcp.tool decorator is a placeholder for the actual Model Context Protocol decorator. +# In a real MCP server, you would import it, e.g., `from mcp import tool`. +class mcp: + def tool(*args, **kwargs): + def decorator(f): + return f + return decorator + +# Set up logging +logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') +logger = logging.getLogger(__name__) + +@mcp.tool() +def run_makefile_pl( + project_dir: Path, + makefile_pl: str = "Makefile.PL", + output_makefile: str = "Makefile", + prefix: Optional[Path] = None, + lib: Optional[Path] = None, + install_base: Optional[Path] = None, + destdir: Optional[Path] = None, + installdirs: Optional[str] = None, + uninst: bool = False, + verbose: bool = False, + linktype: Optional[str] = None, + optimize: Optional[str] = None, + extra_args: Optional[List[str]] = None, +) -> Dict: + """ + Generates a Makefile from a Makefile.PL script using ExtUtils::MakeMaker. + + This tool simulates the 'perl Makefile.PL' command, which is the first step + in the standard Perl module installation process. It allows specifying + common installation and build parameters. + + Args: + project_dir: The directory containing the Makefile.PL script. + makefile_pl: The name of the Perl Makefile script to execute. + output_makefile: The expected name of the generated Makefile. + prefix: Corresponds to PREFIX. Top-level installation directory. + lib: Corresponds to LIB. Directory for architecture-independent files. + install_base: Corresponds to INSTALL_BASE. A simpler way to specify a custom installation location. + destdir: Corresponds to DESTDIR. For staging installations. + installdirs: Corresponds to INSTALLDIRS. Installation scheme ('site', 'vendor', or 'perl'). + uninst: Corresponds to UNINST=1. If true, create an uninstall target. + verbose: Corresponds to VERBOSE=1. If true, enables verbose output. + linktype: Corresponds to LINKTYPE. Link type ('static' or 'dynamic'). + optimize: Corresponds to OPTIMIZE. Compiler optimization flags. + extra_args: A list of additional arguments in 'KEY=VALUE' format. + + Returns: + A dictionary containing the command executed, stdout, stderr, return code, and a list of output files. + """ + # 1. Input Validation + if not project_dir.is_dir(): + raise ValueError(f"Project directory not found: {project_dir}") + + makefile_pl_path = project_dir / makefile_pl + if not makefile_pl_path.is_file(): + raise FileNotFoundError(f"Makefile.PL script not found: {makefile_pl_path}") + + if installdirs and installdirs not in ["site", "vendor", "perl"]: + raise ValueError(f"Invalid value for installdirs: '{installdirs}'. Must be one of 'site', 'vendor', 'perl'.") + + if linktype and linktype not in ["static", "dynamic"]: + raise ValueError(f"Invalid value for linktype: '{linktype}'. Must be one of 'static' or 'dynamic'.") + + # 2. Command Construction + cmd = ["perl", makefile_pl] + + if prefix: + cmd.append(f"PREFIX={prefix.resolve()}") + if lib: + cmd.append(f"LIB={lib.resolve()}") + if install_base: + cmd.append(f"INSTALL_BASE={install_base.resolve()}") + if destdir: + cmd.append(f"DESTDIR={destdir.resolve()}") + if installdirs: + cmd.append(f"INSTALLDIRS={installdirs}") + if uninst: + cmd.append("UNINST=1") + if verbose: + cmd.append("VERBOSE=1") + if linktype: + cmd.append(f"LINKTYPE={linktype}") + if optimize: + cmd.append(f"OPTIMIZE={optimize}") + if extra_args: + for arg in extra_args: + if "=" not in arg or arg.startswith("="): + raise ValueError(f"Invalid extra argument format: '{arg}'. Must be in 'KEY=VALUE' format.") + cmd.extend(extra_args) + + command_executed = " ".join(map(str, cmd)) + logger.info(f"Executing command: {command_executed} in {project_dir}") + + # 3. Subprocess Execution + try: + result = subprocess.run( + cmd, + cwd=project_dir, + capture_output=True, + text=True, + check=True, + ) + except FileNotFoundError: + logger.error("Error: 'perl' command not found. Please ensure Perl is installed and in your PATH.") + raise + except subprocess.CalledProcessError as e: + logger.error(f"Command failed with exit code {e.returncode}") + return { + "command_executed": command_executed, + "stdout": e.stdout, + "stderr": e.stderr, + "return_code": e.returncode, + "output_files": [], + } + + # 4. Output Handling + output_files = [] + generated_makefile_path = project_dir / output_makefile + if generated_makefile_path.exists(): + output_files.append(str(generated_makefile_path)) + logger.info(f"Successfully generated Makefile: {generated_makefile_path}") + else: + logger.warning(f"Expected Makefile not found at: {generated_makefile_path}") + + # 5. Structured Result Return + return { + "command_executed": command_executed, + "stdout": result.stdout, + "stderr": result.stderr, + "return_code": result.returncode, + "output_files": output_files, + } + +@mcp.tool() +def make_build( + project_dir: Path, + makefile: str = "Makefile", + target: str = "all", + jobs: Optional[int] = None, +) -> Dict: + """ + Runs 'make' to build a Perl module after Makefile.PL has been run. + + Args: + project_dir: The directory containing the Makefile. + makefile: The name of the Makefile to use. + target: The make target to build (e.g., 'all', 'clean'). + jobs: The number of parallel jobs to run (corresponds to 'make -j'). + + Returns: + A dictionary containing the command executed, stdout, stderr, and return code. + """ + # 1. Input Validation + if not project_dir.is_dir(): + raise ValueError(f"Project directory not found: {project_dir}") + makefile_path = project_dir / makefile + if not makefile_path.is_file(): + raise FileNotFoundError(f"Makefile not found: {makefile_path}. Did you run 'run_makefile_pl' first?") + if jobs is not None and jobs < 1: + raise ValueError("Number of jobs must be a positive integer.") + + # 2. Command Construction + cmd = ["make", "-f", str(makefile_path.name)] + if jobs: + cmd.append(f"-j{jobs}") + cmd.append(target) + + command_executed = " ".join(cmd) + logger.info(f"Executing command: {command_executed} in {project_dir}") + + # 3. Subprocess Execution + try: + result = subprocess.run( + cmd, + cwd=project_dir, + capture_output=True, + text=True, + check=True, + ) + except FileNotFoundError: + logger.error("Error: 'make' command not found. Please ensure 'make' is installed and in your PATH.") + raise + except subprocess.CalledProcessError as e: + logger.error(f"Make build failed with exit code {e.returncode}") + return { + "command_executed": command_executed, + "stdout": e.stdout, + "stderr": e.stderr, + "return_code": e.returncode, + } + + # 4. Structured Result Return + return { + "command_executed": command_executed, + "stdout": result.stdout, + "stderr": result.stderr, + "return_code": result.returncode, + } + +@mcp.tool() +def make_test( + project_dir: Path, + makefile: str = "Makefile", + verbose: bool = False, +) -> Dict: + """ + Runs 'make test' to execute the test suite for a Perl module. + + Args: + project_dir: The directory containing the Makefile. + makefile: The name of the Makefile to use. + verbose: If true, runs tests verbosely (TEST_VERBOSE=1). + + Returns: + A dictionary containing the command executed, stdout, stderr, and return code. + """ + # 1. Input Validation + if not project_dir.is_dir(): + raise ValueError(f"Project directory not found: {project_dir}") + makefile_path = project_dir / makefile + if not makefile_path.is_file(): + raise FileNotFoundError(f"Makefile not found: {makefile_path}. Did you run 'run_makefile_pl' first?") + + # 2. Command Construction + cmd = ["make", "-f", str(makefile_path.name), "test"] + if verbose: + cmd.append("TEST_VERBOSE=1") + + command_executed = " ".join(cmd) + logger.info(f"Executing command: {command_executed} in {project_dir}") + + # 3. Subprocess Execution + try: + result = subprocess.run( + cmd, + cwd=project_dir, + capture_output=True, + text=True, + check=True, + ) + except FileNotFoundError: + logger.error("Error: 'make' command not found. Please ensure 'make' is installed and in your PATH.") + raise + except subprocess.CalledProcessError as e: + logger.error(f"Make test failed with exit code {e.returncode}") + return { + "command_executed": command_executed, + "stdout": e.stdout, + "stderr": e.stderr, + "return_code": e.returncode, + } + + # 4. Structured Result Return + return { + "command_executed": command_executed, + "stdout": result.stdout, + "stderr": result.stderr, + "return_code": result.returncode, + } + +@mcp.tool() +def make_install( + project_dir: Path, + makefile: str = "Makefile", +) -> Dict: + """ + Runs 'make install' to install a Perl module. + Note: This may require elevated (e.g., root) privileges depending on the installation prefix. + + Args: + project_dir: The directory containing the Makefile. + makefile: The name of the Makefile to use. + + Returns: + A dictionary containing the command executed, stdout, stderr, and return code. + """ + # 1. Input Validation + if not project_dir.is_dir(): + raise ValueError(f"Project directory not found: {project_dir}") + makefile_path = project_dir / makefile + if not makefile_path.is_file(): + raise FileNotFoundError(f"Makefile not found: {makefile_path}. Did you run 'run_makefile_pl' first?") + + # 2. Command Construction + cmd = ["make", "-f", str(makefile_path.name), "install"] + + command_executed = " ".join(cmd) + logger.info(f"Executing command: {command_executed} in {project_dir}") + + # 3. Subprocess Execution + try: + result = subprocess.run( + cmd, + cwd=project_dir, + capture_output=True, + text=True, + check=True, + ) + except FileNotFoundError: + logger.error("Error: 'make' command not found. Please ensure 'make' is installed and in your PATH.") + raise + except subprocess.CalledProcessError as e: + logger.error(f"Make install failed with exit code {e.returncode}") + return { + "command_executed": command_executed, + "stdout": e.stdout, + "stderr": e.stderr, + "return_code": e.returncode, + } + + # 4. Structured Result Return + return { + "command_executed": command_executed, + "stdout": result.stdout, + "stderr": result.stderr, + "return_code": result.returncode, + } \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-extutils-makemaker/app/perl-extutils-makemaker_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-extutils-makemaker/app/perl-extutils-makemaker_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..e11558aa02b36eaf71694b1afdc145c4ba31583d --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-extutils-makemaker/app/perl-extutils-makemaker_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-extutils-makemaker/app/perl-extutils-makemaker_server.py') +SERVER_NAME = 'biosci_perl_extutils_makemaker' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-extutils-makemaker/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-extutils-makemaker/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-extutils-makemaker/app/requirements.txt @@ -0,0 +1 @@ + diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-extutils-makemaker/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-extutils-makemaker/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..7a275d0ddcca2df1beff86dfb541f70a55e24939 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-extutils-makemaker/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-perl-extutils-makemaker: + build: . + image: mcp-perl-extutils-makemaker:latest + container_name: mcp-perl-extutils-makemaker + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=perl-extutils-makemaker + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-extutils-makemaker/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-extutils-makemaker/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..73db9ee6c6f84cbea47adee38cd01ad4b43317e9 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-extutils-makemaker/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - perl-extutils-makemaker + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-extutils-makemaker/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-extutils-makemaker/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-extutils-makemaker/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-font-afm/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-font-afm/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..116e8e5057f57e870ddcd896f47fb333a59465a9 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-font-afm/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install perl-font-afm via conda (e.g., from bioconda) +RUN conda install -c bioconda perl-font-afm -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY app/perl-font-afm_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/perl-font-afm_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/perl-font-afm_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-font-afm/app/perl-font-afm_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-font-afm/app/perl-font-afm_server.py new file mode 100644 index 0000000000000000000000000000000000000000..cf67ac89e903d9a8c4a35ca5b2b8e9073b7ca2af --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-font-afm/app/perl-font-afm_server.py @@ -0,0 +1,209 @@ +import subprocess +import tempfile +from pathlib import Path +from typing import Dict, List, Optional, Union + +# MCP decorator is omitted as per instructions. +# @mcp.tool() +def perl_execute( + programfile: Optional[Path] = None, + arguments: Optional[List[str]] = None, + program_line: Optional[List[str]] = None, + program_line_features: Optional[List[str]] = None, + record_separator: Optional[str] = None, + autosplit: bool = False, + unicode_features: Optional[str] = None, + check_syntax_only: bool = False, + debugger: Optional[str] = None, + debugging_flags: Optional[str] = None, + no_sitecustomize: bool = False, + split_pattern: Optional[str] = None, + edit_in_place: Optional[str] = None, + include_directory: Optional[List[Path]] = None, + line_ending_processing: Optional[str] = None, + use_module: Optional[List[str]] = None, + no_module: Optional[List[str]] = None, + loop_around_program: bool = False, + loop_and_print: bool = False, + enable_switch_parsing: bool = False, + search_path: bool = False, + tainting_warnings: bool = False, + tainting_checks: bool = False, + dump_core: bool = False, + allow_unsafe: bool = False, + print_version: bool = False, + print_config: Optional[str] = None, + enable_warnings: bool = False, + enable_all_warnings: bool = False, + extract_from_text: bool = False, + extract_directory: Optional[Path] = None, + disable_all_warnings: bool = False, +) -> Dict[str, Union[str, List[str]]]: + """ + Executes the Perl interpreter with a wide range of command-line options. + + This tool serves as a wrapper for the `perl` command. While the package is + `perl-font-afm`, it provides a module for Perl, not a direct executable. + This function allows you to run any Perl script or one-liner, including + those that might use the Font::AFM module. + + Args: + programfile: Path to the Perl program file to execute. + arguments: A list of arguments to pass to the program file. + program_line: One or more lines of a program to execute (-e). Omit programfile. + program_line_features: Like -e, but enables all optional features (-E). Omit programfile. + record_separator: Specify record separator (-0[octal]). + autosplit: Autosplit mode with -n or -p (splits $_ into @F) (-a). + unicode_features: Enables the listed Unicode features (-C[number/list]). + check_syntax_only: Check syntax only (runs BEGIN and CHECK blocks) (-c). + debugger: Run program under debugger (-d[:debugger]). Provide an empty string for -d, or a debugger name. + debugging_flags: Set debugging flags (-D[number/list]). + no_sitecustomize: Don't do $sitelib/sitecustomize.pl at startup (-f). + split_pattern: split() pattern for -a switch (-F/pattern/). + edit_in_place: Edit <> files in place (-i[extension]). Provide an empty string for no backup, or an extension like '.bak'. + include_directory: Specify @INC/#include directory (-I). Multiple paths can be provided. + line_ending_processing: Enable line ending processing, specifies line terminator (-l[octal]). + use_module: Execute "use module..." before executing the program (-M). + no_module: Execute "no module..." before executing the program (-M-). + loop_around_program: Assume "while (<>) { ... }" loop around program (-n). + loop_and_print: Assume loop like -n but print line also, like sed (-p). + enable_switch_parsing: Enable rudimentary parsing for switches after programfile (-s). + search_path: Look for programfile using PATH environment variable (-S). + tainting_warnings: Enable tainting warnings (-t). + tainting_checks: Enable tainting checks (-T). + dump_core: Dump core after parsing program (-u). + allow_unsafe: Allow unsafe operations (-U). + print_version: Print version, patchlevel and license (-v). + print_config: Print configuration summary (-V[:variable]). Provide an empty string for summary, or a variable name. + enable_warnings: Enable many useful warnings (-w). + enable_all_warnings: Enable all warnings (-W). + extract_from_text: Ignore text before #!perl line (-x). + extract_directory: Optionally cd to this directory when using -x. + disable_all_warnings: Disable all warnings (-X). + + Returns: + A dictionary containing the command executed, stdout, stderr, and output files. + """ + # --- Input Validation --- + if programfile and (program_line or program_line_features): + raise ValueError("Cannot specify 'programfile' with 'program_line' or 'program_line_features'.") + + is_informational = print_version or (print_config is not None) + if not (programfile or program_line or program_line_features or is_informational): + raise ValueError("Either 'programfile', 'program_line', 'program_line_features', or an informational flag like 'print_version' must be provided.") + + if programfile and not programfile.exists(): + raise FileNotFoundError(f"Program file not found: {programfile}") + + if extract_directory and not extract_from_text: + raise ValueError("'extract_directory' can only be used when 'extract_from_text' is True.") + + if split_pattern and not autosplit: + raise ValueError("'split_pattern' is only effective when 'autosplit' is True.") + + # --- Command Construction --- + cmd = ["perl"] + + if record_separator is not None: + cmd.append(f"-0{record_separator}") + if autosplit: + cmd.append("-a") + if unicode_features is not None: + cmd.append(f"-C{unicode_features}") + if check_syntax_only: + cmd.append("-c") + if debugger is not None: + cmd.append(f"-d:{debugger}" if debugger else "-d") + if debugging_flags is not None: + cmd.append(f"-D{debugging_flags}") + if no_sitecustomize: + cmd.append("-f") + if split_pattern is not None: + cmd.append(f"-F{split_pattern}") + if edit_in_place is not None: + cmd.append(f"-i{edit_in_place}") + if line_ending_processing is not None: + cmd.append(f"-l{line_ending_processing}") + if loop_around_program: + cmd.append("-n") + if loop_and_print: + cmd.append("-p") + if enable_switch_parsing: + cmd.append("-s") + if search_path: + cmd.append("-S") + if tainting_warnings: + cmd.append("-t") + if tainting_checks: + cmd.append("-T") + if dump_core: + cmd.append("-u") + if allow_unsafe: + cmd.append("-U") + if print_version: + cmd.append("-v") + if print_config is not None: + cmd.append(f"-V:{print_config}" if print_config else "-V") + if enable_warnings: + cmd.append("-w") + if enable_all_warnings: + cmd.append("-W") + if disable_all_warnings: + cmd.append("-X") + if extract_from_text: + cmd.append("-x") + if extract_directory: + cmd.append(str(extract_directory)) + + if include_directory: + for directory in include_directory: + cmd.extend(["-I", str(directory)]) + + if use_module: + for mod in use_module: + cmd.append(f"-M{mod}") + + if no_module: + for mod in no_module: + cmd.append(f"-M-{mod}") + + if program_line: + for line in program_line: + cmd.extend(["-e", line]) + + if program_line_features: + for line in program_line_features: + cmd.extend(["-E", line]) + + if arguments and programfile: + cmd.append("--") + + if programfile: + cmd.append(str(programfile)) + + if arguments: + cmd.extend(arguments) + + # --- Subprocess Execution --- + command_executed = " ".join(cmd) + try: + result = subprocess.run( + cmd, + check=True, + capture_output=True, + text=True, + ) + return { + "command_executed": command_executed, + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": [] # Perl execution does not have a default output file + } + except subprocess.CalledProcessError as e: + return { + "command_executed": command_executed, + "stdout": e.stdout, + "stderr": e.stderr, + "error": f"Perl execution failed with return code {e.returncode}", + "output_files": [] + } diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-font-afm/app/perl-font-afm_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-font-afm/app/perl-font-afm_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..50bf07350810079c39e826e3acac15f8adbd303f --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-font-afm/app/perl-font-afm_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-font-afm/app/perl-font-afm_server.py') +SERVER_NAME = 'biosci_perl_font_afm' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-font-afm/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-font-afm/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-font-afm/app/requirements.txt @@ -0,0 +1 @@ + diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-font-afm/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-font-afm/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..be29ccf2d21611fdfa51ff0f2f3198fc39b658f1 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-font-afm/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-perl-font-afm: + build: . + image: mcp-perl-font-afm:latest + container_name: mcp-perl-font-afm + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=perl-font-afm + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-font-afm/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-font-afm/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..c3fc76220c21b2d3ab1caa23f6dfe0eb00c9a4b9 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-font-afm/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - perl-font-afm + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-font-afm/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-font-afm/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-font-afm/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-html-element-extended/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-html-element-extended/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..8b6feeb43952d36e40c934de90248a4c53685b9a --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-html-element-extended/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install perl-html-element-extended via conda (e.g., from bioconda) +RUN conda install -c bioconda perl-html-element-extended -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY app/perl-html-element-extended_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/perl-html-element-extended_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/perl-html-element-extended_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-html-element-extended/app/perl-html-element-extended_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-html-element-extended/app/perl-html-element-extended_server.py new file mode 100644 index 0000000000000000000000000000000000000000..d756ec96e08a92e1c528cc618df098db6bffefea --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-html-element-extended/app/perl-html-element-extended_server.py @@ -0,0 +1,205 @@ +import subprocess +import tempfile +from pathlib import Path +from typing import List, Optional + +# @mcp.tool() +def perl_run( + programfile: Optional[Path] = None, + arguments: Optional[List[str]] = None, + execute_program: Optional[List[str]] = None, + execute_program_extended: Optional[List[str]] = None, + record_separator: Optional[str] = None, + autosplit: bool = False, + unicode_features: Optional[str] = None, + check_syntax_only: bool = False, + run_under_debugger: Optional[str] = None, + debugging_flags: Optional[str] = None, + no_sitecustomize: bool = False, + split_pattern: Optional[str] = None, + edit_in_place: Optional[str] = None, + include_directory: Optional[List[Path]] = None, + use_module: Optional[List[str]] = None, + no_module: Optional[List[str]] = None, + use_module_no_import: Optional[List[str]] = None, + no_module_no_import: Optional[List[str]] = None, + loop_around_program: bool = False, + loop_and_print: bool = False, + enable_switch_parsing: bool = False, + search_path_for_program: bool = False, + tainting_warnings: bool = False, + tainting_checks: bool = False, + dump_core: bool = False, + allow_unsafe_operations: bool = False, + print_version: bool = False, + print_config: Optional[str] = None, + enable_warnings: bool = False, + enable_all_warnings: bool = False, + ignore_text_before_shebang: Optional[str] = None, + disable_all_warnings: bool = False, +) -> dict: + """ + Wraps the Perl interpreter command-line interface. Note: The 'perl-html-element-extended' + package is a library and does not provide a standalone executable. This tool exposes the + functionality of the underlying 'perl' interpreter. + + Args: + programfile: Path to the Perl program file to execute. + arguments: A list of arguments to pass to the program file. + execute_program: One or more lines of a program to be executed. Omit programfile. + execute_program_extended: Like -e, but enables all optional features. + record_separator: Specify record separator (e.g., '0777' for slurp mode). + autosplit: Enable autosplit mode with -n or -p (splits $_ into @F). + unicode_features: Enable the listed Unicode features (e.g., 'C,S,D'). + check_syntax_only: Check syntax only (runs BEGIN and CHECK blocks). + run_under_debugger: Run program under debugger. Can specify a debugger (e.g., ':Devel::NYTProf'). + debugging_flags: Set debugging flags (bit mask or alphabets). + no_sitecustomize: Don't do $sitelib/sitecustomize.pl at startup. + split_pattern: split() pattern for -a switch. + edit_in_place: Edit <> files in place. Provide an extension for backup (e.g., '.bak'). + include_directory: Specify @INC/#include directory. Multiple paths can be provided. + use_module: Execute "use module..." before executing the program. + no_module: Execute "no module..." before executing the program. + use_module_no_import: Execute "use module ()" before executing the program. + no_module_no_import: Execute "no module ()" before executing the program. + loop_around_program: Assume "while (<>) { ... }" loop around the program. + loop_and_print: Assume loop like -n but also print line, like sed. + enable_switch_parsing: Enable rudimentary parsing for switches after programfile. + search_path_for_program: Look for programfile using the PATH environment variable. + tainting_warnings: Enable tainting warnings. + tainting_checks: Enable tainting checks. + dump_core: Dump core after parsing the program. + allow_unsafe_operations: Allow unsafe operations. + print_version: Print version, patchlevel, and license. + print_config: Print configuration summary or a single Config.pm variable. + enable_warnings: Enable many useful warnings. + enable_all_warnings: Enable all warnings. + ignore_text_before_shebang: Ignore text before #!perl line. Can optionally specify a directory to cd into. + disable_all_warnings: Disable all warnings. + + Returns: + A dictionary containing the executed command, stdout, stderr, and a list of output files. + """ + # Input validation + if not any([programfile, execute_program, execute_program_extended, print_version, print_config is not None]): + raise ValueError("Either 'programfile', 'execute_program', 'execute_program_extended', 'print_version', or 'print_config' must be provided.") + + if programfile and (execute_program or execute_program_extended): + raise ValueError("'programfile' cannot be used with 'execute_program' or 'execute_program_extended'.") + + if programfile and not programfile.is_file(): + raise FileNotFoundError(f"Program file not found: {programfile}") + + cmd = ["perl"] + + # Build command from parameters + if record_separator is not None: + cmd.append(f"-0{record_separator}") + if autosplit: + cmd.append("-a") + if unicode_features is not None: + cmd.append(f"-C{unicode_features}") + if check_syntax_only: + cmd.append("-c") + if run_under_debugger is not None: + cmd.append(f"-d{run_under_debugger}") + if debugging_flags is not None: + cmd.append(f"-D{debugging_flags}") + if no_sitecustomize: + cmd.append("-f") + if split_pattern is not None: + cmd.append(f"-F{split_pattern}") + if edit_in_place is not None: + cmd.append(f"-i{edit_in_place}") + if include_directory: + for directory in include_directory: + cmd.extend(["-I", str(directory)]) + if use_module: + for module in use_module: + cmd.append(f"-M{module}") + if no_module: + for module in no_module: + cmd.append(f"-M-{module}") + if use_module_no_import: + for module in use_module_no_import: + cmd.append(f"-m{module}") + if no_module_no_import: + for module in no_module_no_import: + cmd.append(f"-m-{module}") + if loop_around_program: + cmd.append("-n") + if loop_and_print: + cmd.append("-p") + if enable_switch_parsing: + cmd.append("-s") + if search_path_for_program: + cmd.append("-S") + if tainting_warnings: + cmd.append("-t") + if tainting_checks: + cmd.append("-T") + if dump_core: + cmd.append("-u") + if allow_unsafe_operations: + cmd.append("-U") + if print_version: + cmd.append("-v") + if print_config is not None: + cmd.append(f"-V{print_config}") + if enable_warnings: + cmd.append("-w") + if enable_all_warnings: + cmd.append("-W") + if ignore_text_before_shebang is not None: + cmd.append(f"-x{ignore_text_before_shebang}") + if disable_all_warnings: + cmd.append("-X") + + if execute_program: + for prog_line in execute_program: + cmd.extend(["-e", prog_line]) + + if execute_program_extended: + for prog_line in execute_program_extended: + cmd.extend(["-E", prog_line]) + + if programfile: + cmd.append(str(programfile)) + + if arguments: + cmd.extend(arguments) + + command_executed = " ".join(cmd) + + try: + result = subprocess.run( + cmd, + capture_output=True, + text=True, + check=True + ) + stdout = result.stdout + stderr = result.stderr + except subprocess.CalledProcessError as e: + return { + "command_executed": command_executed, + "stdout": e.stdout, + "stderr": e.stderr, + "error": f"Perl execution failed with return code {e.returncode}", + "output_files": [] + } + except FileNotFoundError: + return { + "command_executed": command_executed, + "stdout": "", + "stderr": "perl command not found. Please ensure Perl is installed and in the system's PATH.", + "error": "Command not found", + "output_files": [] + } + + return { + "command_executed": command_executed, + "stdout": stdout, + "stderr": stderr, + "output_files": [] # Perl interpreter itself doesn't guarantee output files. + } \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-html-element-extended/app/perl-html-element-extended_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-html-element-extended/app/perl-html-element-extended_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..c7e7e236ee746f94648af95f63df0d40c6e8ed43 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-html-element-extended/app/perl-html-element-extended_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-html-element-extended/app/perl-html-element-extended_server.py') +SERVER_NAME = 'biosci_perl_html_element_extended' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-html-element-extended/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-html-element-extended/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-html-element-extended/app/requirements.txt @@ -0,0 +1 @@ + diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-html-element-extended/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-html-element-extended/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..64a618981e9115de3ee271c2e476ff314c3a80b0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-html-element-extended/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-perl-html-element-extended: + build: . + image: mcp-perl-html-element-extended:latest + container_name: mcp-perl-html-element-extended + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=perl-html-element-extended + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-html-element-extended/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-html-element-extended/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..dc93a4a5f6349e69e90ae3f76c3d4af152844fc2 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-html-element-extended/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - perl-html-element-extended + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-html-element-extended/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-html-element-extended/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-html-element-extended/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-html-tagset/app/perl-html-tagset_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-html-tagset/app/perl-html-tagset_server.py new file mode 100644 index 0000000000000000000000000000000000000000..3d16299108849841dc2fde455840367ae05ce87e --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-html-tagset/app/perl-html-tagset_server.py @@ -0,0 +1,282 @@ +import subprocess +import tempfile +import json +import logging +from pathlib import Path +from typing import Dict, Any, List, Optional + +# --- Tool 1: Specialized wrapper for HTML::Tagset module --- + +# This Perl script template is used by the html_tagset_query tool. +# It uses the HTML::Tagset module to gather properties for a given tag +# and prints them as a JSON object. This requires the 'perl-json' package +# to be available in the execution environment. +PERL_SCRIPT_TEMPLATE = """ +use strict; +use warnings; +use HTML::Tagset; +use JSON; + +# Ensure we have exactly one argument +die "Usage: $0 \\n" unless @ARGV == 1; + +my $tag = lc($ARGV[0]); +my %properties; + +# Check various boolean properties from HTML::Tagset hashes +$properties{{'is_known'}} = exists $HTML::Tagset::isKnown{{$tag}}; +$properties{{'is_empty_element'}} = exists $HTML::Tagset::emptyElement{{$tag}}; +$properties{{'has_optional_end_tag'}} = exists $HTML::Tagset::optionalEndTag{{$tag}}; +$properties{{'is_phrase_markup'}} = exists $HTML::Tagset::isPhraseMarkup{{$tag}}; +$properties{{'is_possible_strict_p_content'}} = exists $HTML::Tagset::is_Possible_Strict_P_Content{{$tag}}; +$properties{{'is_head_element'}} = exists $HTML::Tagset::isHeadElement{{$tag}}; +$properties{{'is_list_container'}} = exists $HTML::Tagset::isList{{$tag}}; +$properties{{'is_table_element'}} = exists $HTML::Tagset::isTableElement{{$tag}}; +$properties{{'is_form_element'}} = exists $HTML::Tagset::isFormElement{{$tag}}; +$properties{{'is_body_element'}} = exists $HTML::Tagset::isBodyElement{{$tag}}; +$properties{{'is_head_or_body_element'}} = exists $HTML::Tagset::isHeadOrBodyElement{{$tag}}; +$properties{{'can_tighten'}} = exists $HTML::Tagset::canTighten{{$tag}}; +$properties{{'is_cdata_parent'}} = exists $HTML::Tagset::isCDATA_Parent{{$tag}}; + +# Handle hashes that contain more complex data (arrays of attributes) +if (exists $HTML::Tagset::linkElements{{$tag}}) {{ + $properties{{'link_attributes'}} = $HTML::Tagset::linkElements{{$tag}}; +}} else {{ + $properties{{'link_attributes'}} = []; +}} + +if (exists $HTML::Tagset::boolean_attr{{$tag}}) {{ + my $attr = $HTML::Tagset::boolean_attr{{$tag}}; + if (ref($attr) eq 'HASH') {{ + $properties{{'boolean_attributes'}} = [keys %$attr]; + }} else {{ + $properties{{'boolean_attributes'}} = [$attr]; + }} +}} else {{ + $properties{{'boolean_attributes'}} = []; +}} + +# Encode the properties hash as JSON and print to stdout +my $json = JSON->new->pretty->encode(\\%properties); +print $json; +""" + +@mcp.tool() +def html_tagset_query(tag_name: str) -> Dict[str, Any]: + """ + Queries the HTML::Tagset Perl module for properties of a given HTML tag. + + This tool dynamically creates and executes a Perl script to interface with the + HTML::Tagset library. It returns a structured JSON object detailing various + properties of the specified tag (e.g., is it an empty element, what are its + link attributes, etc.). + + Args: + tag_name: The HTML tag name to query (e.g., 'p', 'img', 'a'). Case-insensitive. + + Returns: + A dictionary containing the command executed, stdout, stderr, and a + 'tag_properties' key with the parsed properties of the HTML tag. + """ + if not tag_name or not tag_name.strip(): + raise ValueError("tag_name cannot be empty.") + + tag_name_lower = tag_name.strip().lower() + + command = [] + result_dict = { + "command_executed": "", + "stdout": "", + "stderr": "", + "tag_properties": None, + "output_files": [] + } + script_path = None + + try: + with tempfile.NamedTemporaryFile(mode='w', delete=False, suffix='.pl', encoding='utf-8') as tmp_script: + script_path = Path(tmp_script.name) + tmp_script.write(PERL_SCRIPT_TEMPLATE) + + command = ['perl', str(script_path), tag_name_lower] + command_str = " ".join(command) + result_dict["command_executed"] = command_str + + process = subprocess.run( + command, + capture_output=True, + text=True, + check=True, + encoding='utf-8' + ) + + result_dict["stdout"] = process.stdout + result_dict["stderr"] = process.stderr + + if process.stdout: + result_dict["tag_properties"] = json.loads(process.stdout) + + except FileNotFoundError: + result_dict["stderr"] = "Error: 'perl' command not found. Is Perl installed and in your PATH?" + except subprocess.CalledProcessError as e: + result_dict["stdout"] = e.stdout + result_dict["stderr"] = e.stderr + except json.JSONDecodeError as e: + err_msg = f"Failed to parse JSON from Perl script output: {e}" + result_dict["stderr"] = (result_dict.get("stderr", "") + "\n" + err_msg).strip() + except Exception as e: + result_dict["stderr"] = f"An unexpected error occurred: {e}" + finally: + if script_path and script_path.exists(): + script_path.unlink() + + return result_dict + + +# --- Tool 2: Generic wrapper for the Perl interpreter --- + +@mcp.tool() +def run_perl( + program_file: Optional[Path] = None, + program_line: Optional[str] = None, + program_line_features: Optional[str] = None, + arguments: Optional[List[str]] = None, + check_syntax_only: bool = False, + enable_warnings: bool = False, + enable_all_warnings: bool = False, + disable_all_warnings: bool = False, + enable_tainting_warnings: bool = False, + enable_tainting_checks: bool = False, + include_directories: Optional[List[Path]] = None, + use_module: Optional[List[str]] = None, + no_module: Optional[List[str]] = None, + autosplit: bool = False, + split_pattern: Optional[str] = None, + loop_around_program: bool = False, + loop_and_print: bool = False, + edit_in_place: Optional[str] = None, + enable_line_ending_processing: Optional[str] = None, + record_separator: Optional[str] = None, + search_path_for_program: bool = False, + print_version: bool = False, + print_config: Optional[str] = None, + run_under_debugger: Optional[str] = None, + enable_unicode_features: Optional[str] = None, + debugging_flags: Optional[str] = None +) -> Dict[str, Any]: + """ + Executes a Perl script or one-liner using the Perl interpreter. + + This tool is a general-purpose wrapper for the `perl` command, providing access + to its most common command-line switches as seen in `perl -h`. + + Args: + program_file: Path to the Perl program file to execute. + program_line: A line of a program to be executed (-e). + program_line_features: Like -e, but enables all optional features (-E). + arguments: A list of arguments to be passed to the program. + check_syntax_only: Check syntax only (-c). + enable_warnings: Enable many useful warnings (-w). + enable_all_warnings: Enable all warnings (-W). + disable_all_warnings: Disable all warnings (-X). + enable_tainting_warnings: Enable tainting warnings (-t). + enable_tainting_checks: Enable tainting checks (-T). + include_directories: Specify @INC/#include directories (-I). + use_module: Execute "use module..." before the program (-M). + no_module: Execute "no module..." before the program (-m). + autosplit: Autosplit mode with -n or -p (-a). + split_pattern: split() pattern for -a switch (-F). + loop_around_program: Assume "while (<>) { ... }" loop (-n). + loop_and_print: Assume loop like -n but also print line (-p). + edit_in_place: Edit <> files in place, with optional backup extension (-i). + enable_line_ending_processing: Enable line ending processing (-l). + record_separator: Specify record separator (-0). + search_path_for_program: Look for programfile using PATH (-S). + print_version: Print version, patchlevel and license (-v). + print_config: Print configuration summary or a single variable (-V). + run_under_debugger: Run program under debugger (-d). + enable_unicode_features: Enables the listed Unicode features (-C). + debugging_flags: Set debugging flags (-D). + + Returns: + A dictionary containing the command executed, stdout, and stderr. + """ + # Input validation + program_sources = sum(p is not None for p in [program_file, program_line, program_line_features]) + if program_sources > 1: + raise ValueError("Only one of 'program_file', 'program_line', or 'program_line_features' can be specified.") + if program_sources == 0 and not print_version and print_config is None: + raise ValueError("A program source ('program_file', 'program_line', 'program_line_features') or an info flag ('print_version', 'print_config') must be provided.") + + command = ['perl'] + + # Add flags + if check_syntax_only: command.append('-c') + if enable_warnings: command.append('-w') + if enable_all_warnings: command.append('-W') + if disable_all_warnings: command.append('-X') + if enable_tainting_warnings: command.append('-t') + if enable_tainting_checks: command.append('-T') + if autosplit: command.append('-a') + if loop_around_program: command.append('-n') + if loop_and_print: command.append('-p') + if search_path_for_program: command.append('-S') + if print_version: command.append('-v') + + # Add flags with arguments + if record_separator is not None: command.append(f'-0{record_separator}') + if enable_unicode_features is not None: command.append(f'-C{enable_unicode_features}') + if debugging_flags is not None: command.append(f'-D{debugging_flags}') + if program_line is not None: command.extend(['-e', program_line]) + if program_line_features is not None: command.extend(['-E', program_line_features]) + if split_pattern is not None: command.append(f'-F{split_pattern}') + if edit_in_place is not None: command.append(f'-i{edit_in_place}') + if enable_line_ending_processing is not None: command.append(f'-l{enable_line_ending_processing}') + if print_config is not None: command.append(f'-V{print_config}') + if run_under_debugger is not None: command.append(f'-d{run_under_debugger}') + + # Add list-based flags + if include_directories: + for directory in include_directories: + command.extend(['-I', str(directory)]) + if use_module: + for mod in use_module: + command.extend(['-M', mod]) + if no_module: + for mod in no_module: + command.extend(['-m', mod]) + + # Add program file and arguments + if program_file: + command.append(str(program_file)) + + if arguments: + command.append('--') + command.extend(arguments) + + result_dict = { + "command_executed": " ".join(command), + "stdout": "", + "stderr": "", + "output_files": [] + } + + try: + process = subprocess.run( + command, + capture_output=True, + text=True, + check=True, + encoding='utf-8' + ) + result_dict["stdout"] = process.stdout + result_dict["stderr"] = process.stderr + except FileNotFoundError: + result_dict["stderr"] = "Error: 'perl' command not found. Is Perl installed and in your PATH?" + except subprocess.CalledProcessError as e: + result_dict["stdout"] = e.stdout + result_dict["stderr"] = e.stderr + except Exception as e: + result_dict["stderr"] = f"An unexpected error occurred: {e}" + + return result_dict \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-html-tidy/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-html-tidy/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..c26f55dadf2fbbc25a520d36eb64428d030579f0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-html-tidy/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install perl-html-tidy via conda (e.g., from bioconda) +RUN conda install -c bioconda perl-html-tidy -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY app/perl-html-tidy_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/perl-html-tidy_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/perl-html-tidy_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-html-tidy/app/perl-html-tidy_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-html-tidy/app/perl-html-tidy_server.py new file mode 100644 index 0000000000000000000000000000000000000000..f2d6acf3895d60b99b3053dd8985566a752b07eb --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-html-tidy/app/perl-html-tidy_server.py @@ -0,0 +1,173 @@ +import subprocess +import tempfile +from pathlib import Path +from typing import List, Optional + +# @mcp.tool() decorator is not needed for the final code as per instructions. +# It is assumed to be present in the execution environment. + +def perl_interpreter( + programfile: Optional[Path] = None, + arguments: Optional[List[str]] = None, + execute_program: Optional[List[str]] = None, + execute_program_extended: Optional[List[str]] = None, + record_separator: Optional[str] = None, + autosplit: bool = False, + unicode_features: Optional[str] = None, + check_syntax_only: bool = False, + run_debugger: Optional[str] = None, + debugging_flags: Optional[str] = None, + no_sitecustomize: bool = False, + split_pattern: Optional[str] = None, + in_place_edit: Optional[str] = None, + include_directories: Optional[List[Path]] = None, + line_ending_processing: Optional[str] = None, + modules_use: Optional[List[str]] = None, + modules_use_no_import: Optional[List[str]] = None, + loop_around_program: bool = False, + loop_and_print: bool = False, + parse_switches: bool = False, + search_path: bool = False, + tainting_warnings: bool = False, + tainting_checks: bool = False, + dump_core: bool = False, + allow_unsafe: bool = False, + print_version: bool = False, + print_config: Optional[str] = None, + enable_warnings: bool = False, + enable_all_warnings: bool = False, + disable_all_warnings: bool = False, + ignore_pre_script_text: Optional[str] = None, +) -> dict: + """ + Executes the Perl interpreter with a given script or command. + + This tool is a wrapper for the 'perl' command-line interpreter. + It allows running Perl scripts or one-liners with a wide range of interpreter options. + Note: The original tool request was for 'perl-html-tidy', but the provided + help documentation was for the generic 'perl' interpreter. + """ + # --- Input Validation --- + if not programfile and not execute_program and not execute_program_extended and not print_version and not print_config: + raise ValueError("Either 'programfile', 'execute_program', 'execute_program_extended', 'print_version', or 'print_config' must be provided.") + + if programfile and (execute_program or execute_program_extended): + raise ValueError("'programfile' cannot be used with 'execute_program' or 'execute_program_extended'.") + + if programfile: + if not programfile.is_file(): + raise FileNotFoundError(f"Program file not found: {programfile}") + + if arguments and not programfile: + raise ValueError("'arguments' can only be provided when a 'programfile' is specified.") + + warning_flags = [enable_warnings, enable_all_warnings, disable_all_warnings] + if sum(warning_flags) > 1: + raise ValueError("Only one of 'enable_warnings', 'enable_all_warnings', or 'disable_all_warnings' can be True.") + + # --- Command Construction --- + cmd = ["perl"] + + if record_separator is not None: + cmd.append(f"-0{record_separator}") + if autosplit: + cmd.append("-a") + if unicode_features is not None: + cmd.append(f"-C{unicode_features}") + if check_syntax_only: + cmd.append("-c") + if run_debugger is not None: + cmd.append(f"-d:{run_debugger}" if run_debugger else "-d") + if debugging_flags is not None: + cmd.append(f"-D{debugging_flags}") + if execute_program: + for prog in execute_program: + cmd.extend(["-e", prog]) + if execute_program_extended: + for prog in execute_program_extended: + cmd.extend(["-E", prog]) + if no_sitecustomize: + cmd.append("-f") + if split_pattern is not None: + cmd.append(f"-F{split_pattern}") + if in_place_edit is not None: + cmd.append(f"-i{in_place_edit}") + if include_directories: + for directory in include_directories: + cmd.extend(["-I", str(directory)]) + if line_ending_processing is not None: + cmd.append(f"-l{line_ending_processing}") + if modules_use: + for module in modules_use: + cmd.append(f"-M{module}") + if modules_use_no_import: + for module in modules_use_no_import: + cmd.append(f"-m{module}") + if loop_around_program: + cmd.append("-n") + if loop_and_print: + cmd.append("-p") + if parse_switches: + cmd.append("-s") + if search_path: + cmd.append("-S") + if tainting_warnings: + cmd.append("-t") + if tainting_checks: + cmd.append("-T") + if dump_core: + cmd.append("-u") + if allow_unsafe: + cmd.append("-U") + if print_version: + cmd.append("-v") + if print_config is not None: + cmd.append(f"-V:{print_config}" if print_config else "-V") + if enable_warnings: + cmd.append("-w") + if enable_all_warnings: + cmd.append("-W") + if disable_all_warnings: + cmd.append("-X") + if ignore_pre_script_text is not None: + cmd.append(f"-x{ignore_pre_script_text}" if ignore_pre_script_text else "-x") + + if programfile: + cmd.append(str(programfile)) + + if arguments: + cmd.extend(arguments) + + # --- Subprocess Execution --- + command_executed = " ".join(cmd) + try: + result = subprocess.run( + cmd, + capture_output=True, + text=True, + check=True + ) + stdout = result.stdout + stderr = result.stderr + except FileNotFoundError: + return { + "command_executed": command_executed, + "stdout": "", + "stderr": "Error: 'perl' command not found. Please ensure Perl is installed and in the system's PATH.", + "output_files": [] + } + except subprocess.CalledProcessError as e: + return { + "command_executed": command_executed, + "stdout": e.stdout, + "stderr": e.stderr, + "output_files": [] + } + + # --- Structured Result Return --- + return { + "command_executed": command_executed, + "stdout": stdout, + "stderr": stderr, + "output_files": [] + } \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-html-tidy/app/perl-html-tidy_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-html-tidy/app/perl-html-tidy_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..341c14132a67962b0fb26b8bd32a8d2122fca9f6 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-html-tidy/app/perl-html-tidy_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-html-tidy/app/perl-html-tidy_server.py') +SERVER_NAME = 'biosci_perl_html_tidy' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-html-tidy/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-html-tidy/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-html-tidy/app/requirements.txt @@ -0,0 +1 @@ + diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-html-tidy/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-html-tidy/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..e975547ee8d5272952c93b6961bcc1ffe6f04fb0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-html-tidy/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-perl-html-tidy: + build: . + image: mcp-perl-html-tidy:latest + container_name: mcp-perl-html-tidy + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=perl-html-tidy + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-html-tidy/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-html-tidy/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..e2b8e4506f7de60f10933827abe7063919afe1fa --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-html-tidy/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - perl-html-tidy + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-html-tidy/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-html-tidy/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-html-tidy/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-http-date/app/perl-http-date_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-http-date/app/perl-http-date_server.py new file mode 100644 index 0000000000000000000000000000000000000000..0c6dc1f75c45f0813c595348a909df8274886909 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-http-date/app/perl-http-date_server.py @@ -0,0 +1,289 @@ +import subprocess +import sys +from pathlib import Path +from typing import Optional, List, Dict, Any + +# Mock decorator for standalone execution +class mcp: + @staticmethod + def tool(func): + return func + +@mcp.tool +def time2str(time: Optional[int] = None) -> Dict[str, Any]: + """ + Converts a machine time (seconds since epoch) to an HTTP-formatted string. + + This function wraps the `time2str()` function from the Perl HTTP::Date module. + The string returned is in the format preferred for the HTTP protocol (RFC 1123), + represented in Universal Time (GMT). Example: Sun, 06 Nov 1994 08:49:37 GMT. + + Args: + time: The time in seconds since the epoch. If not provided or None, + the current time will be used. + + Returns: + A dictionary containing the command executed, stdout (the formatted date string), + stderr, and a list of output files (which is always empty). + """ + command = ["perl", "-MHTTP::Date", "-e"] + if time is not None: + if not isinstance(time, int): + raise TypeError("The 'time' parameter must be an integer.") + script = f'print HTTP::Date::time2str({time})' + else: + script = 'print HTTP::Date::time2str()' + + command.append(script) + + command_str = " ".join(command) + + try: + result = subprocess.run( + command, + capture_output=True, + text=True, + check=True, + ) + return { + "command_executed": command_str, + "stdout": result.stdout.strip(), + "stderr": result.stderr.strip(), + "output_files": [] + } + except FileNotFoundError: + return { + "command_executed": command_str, + "stdout": "", + "stderr": "Error: 'perl' executable not found. Please ensure Perl is installed and in your PATH.", + "output_files": [] + } + except subprocess.CalledProcessError as e: + return { + "command_executed": command_str, + "stdout": e.stdout.strip(), + "stderr": e.stderr.strip(), + "output_files": [] + } + +@mcp.tool +def str2time(date_string: str, zone: Optional[str] = None) -> Dict[str, Any]: + """ + Converts a date string to machine time (seconds since epoch). + + This function wraps the `str2time()` function from the Perl HTTP::Date module. + It returns the machine time if the format is recognized, otherwise it may return an empty string. + Note: If the zone is not 'GMT' or numerical (e.g., '-0800'), the Time::Zone Perl module + must be installed for the date to be recognized. + + Args: + date_string: The date string to convert. + zone: The default time zone to use if the date string does not contain one. + + Returns: + A dictionary containing the command executed, stdout (the machine time), + stderr, and a list of output files (which is always empty). + """ + if not date_string: + raise ValueError("The 'date_string' parameter cannot be empty.") + + command = ["perl", "-MHTTP::Date", "-e"] + + if zone: + script = 'print HTTP::Date::str2time(shift, shift)' + command.extend([script, date_string, zone]) + else: + script = 'print HTTP::Date::str2time(shift)' + command.extend([script, date_string]) + + command_str = " ".join(f"'{arg}'" if " " in arg else arg for arg in command) + + try: + result = subprocess.run( + command, + capture_output=True, + text=True, + check=True, + ) + return { + "command_executed": command_str, + "stdout": result.stdout.strip(), + "stderr": result.stderr.strip(), + "output_files": [] + } + except FileNotFoundError: + return { + "command_executed": command_str, + "stdout": "", + "stderr": "Error: 'perl' executable not found. Please ensure Perl is installed and in your PATH.", + "output_files": [] + } + except subprocess.CalledProcessError as e: + return { + "command_executed": command_str, + "stdout": e.stdout.strip(), + "stderr": e.stderr.strip(), + "output_files": [] + } + +@mcp.tool +def parse_date(date_string: str) -> Dict[str, Any]: + """ + Parses a date string and returns its components. + + This function wraps the `parse_date()` function from the Perl HTTP::Date module. + It returns a comma-separated string of numerical values: + year,month,day,hour,min,sec,timezone. The year will be the full 4-digit year, + and month numbers start with 1 for January. + + Args: + date_string: The date string to parse. + + Returns: + A dictionary containing the command executed, stdout (the parsed date components), + stderr, and a list of output files (which is always empty). + """ + if not date_string: + raise ValueError("The 'date_string' parameter cannot be empty.") + + script = 'my @parts = HTTP::Date::parse_date(shift); print join(",", @parts);' + command = ["perl", "-MHTTP::Date", "-e", script, date_string] + command_str = " ".join(f"'{arg}'" if " " in arg else arg for arg in command) + + try: + result = subprocess.run( + command, + capture_output=True, + text=True, + check=True, + ) + return { + "command_executed": command_str, + "stdout": result.stdout.strip(), + "stderr": result.stderr.strip(), + "output_files": [] + } + except FileNotFoundError: + return { + "command_executed": command_str, + "stdout": "", + "stderr": "Error: 'perl' executable not found. Please ensure Perl is installed and in your PATH.", + "output_files": [] + } + except subprocess.CalledProcessError as e: + return { + "command_executed": command_str, + "stdout": e.stdout.strip(), + "stderr": e.stderr.strip(), + "output_files": [] + } + +@mcp.tool +def time2iso(time: Optional[int] = None) -> Dict[str, Any]: + """ + Converts machine time to a 'YYYY-MM-DD hh:mm:ss' formatted string in the local time zone. + + This function wraps the `time2iso()` function from the Perl HTTP::Date module. + + Args: + time: The time in seconds since the epoch. If not provided or None, + the current time will be used. + + Returns: + A dictionary containing the command executed, stdout (the formatted date string), + stderr, and a list of output files (which is always empty). + """ + command = ["perl", "-MHTTP::Date", "-e"] + if time is not None: + if not isinstance(time, int): + raise TypeError("The 'time' parameter must be an integer.") + script = f'print HTTP::Date::time2iso({time})' + else: + script = 'print HTTP::Date::time2iso()' + + command.append(script) + + command_str = " ".join(command) + + try: + result = subprocess.run( + command, + capture_output=True, + text=True, + check=True, + ) + return { + "command_executed": command_str, + "stdout": result.stdout.strip(), + "stderr": result.stderr.strip(), + "output_files": [] + } + except FileNotFoundError: + return { + "command_executed": command_str, + "stdout": "", + "stderr": "Error: 'perl' executable not found. Please ensure Perl is installed and in your PATH.", + "output_files": [] + } + except subprocess.CalledProcessError as e: + return { + "command_executed": command_str, + "stdout": e.stdout.strip(), + "stderr": e.stderr.strip(), + "output_files": [] + } + +@mcp.tool +def time2isoz(time: Optional[int] = None) -> Dict[str, Any]: + """ + Converts machine time to a 'YYYY-MM-DD hh:mm:ssZ' formatted string in Universal Time. + + This function wraps the `time2isoz()` function from the Perl HTTP::Date module. + + Args: + time: The time in seconds since the epoch. If not provided or None, + the current time will be used. + + Returns: + A dictionary containing the command executed, stdout (the formatted date string), + stderr, and a list of output files (which is always empty). + """ + command = ["perl", "-MHTTP::Date", "-e"] + if time is not None: + if not isinstance(time, int): + raise TypeError("The 'time' parameter must be an integer.") + script = f'print HTTP::Date::time2isoz({time})' + else: + script = 'print HTTP::Date::time2isoz()' + + command.append(script) + + command_str = " ".join(command) + + try: + result = subprocess.run( + command, + capture_output=True, + text=True, + check=True, + ) + return { + "command_executed": command_str, + "stdout": result.stdout.strip(), + "stderr": result.stderr.strip(), + "output_files": [] + } + except FileNotFoundError: + return { + "command_executed": command_str, + "stdout": "", + "stderr": "Error: 'perl' executable not found. Please ensure Perl is installed and in your PATH.", + "output_files": [] + } + except subprocess.CalledProcessError as e: + return { + "command_executed": command_str, + "stdout": e.stdout.strip(), + "stderr": e.stderr.strip(), + "output_files": [] + } \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-http-date/app/perl-http-date_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-http-date/app/perl-http-date_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..94c8102e12a08d33824ba7b7b06d3d98c9cf72d6 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-http-date/app/perl-http-date_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-http-date/app/perl-http-date_server.py') +SERVER_NAME = 'biosci_perl_http_date' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-http-date/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-http-date/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-http-date/app/requirements.txt @@ -0,0 +1 @@ + diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-http-date/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-http-date/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..2106b65b2b52c30950736ca80067f61210cd50c6 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-http-date/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - perl-http-date + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-http-date/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-http-date/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-http-date/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-http-negotiate/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-http-negotiate/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..6b8d5bb23c9620ee247d54cb37141589c3b78733 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-http-negotiate/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install perl-http-negotiate via conda (e.g., from bioconda) +RUN conda install -c bioconda perl-http-negotiate -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY app/perl-http-negotiate_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/perl-http-negotiate_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/perl-http-negotiate_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-http-negotiate/app/perl-http-negotiate_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-http-negotiate/app/perl-http-negotiate_server.py new file mode 100644 index 0000000000000000000000000000000000000000..9d25d730710ce2999a9472d5148f0920ffda563f --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-http-negotiate/app/perl-http-negotiate_server.py @@ -0,0 +1,186 @@ +import subprocess +import shlex +from pathlib import Path +from typing import Optional, List + +# This is a placeholder for the MCP decorator. +# In a real MCP environment, this would be imported. +def mcp_tool_placeholder(): + def decorator(func): + return func + return decorator + +mcp = type('MCP', (), {'tool': mcp_tool_placeholder}) + +@mcp.tool() +def run_perl( + programfile: Optional[Path] = None, + arguments: Optional[List[str]] = None, + execute_program: Optional[List[str]] = None, + execute_program_with_features: Optional[List[str]] = None, + check_syntax_only: bool = False, + print_version: bool = False, + print_config: Optional[str] = None, + record_separator: Optional[str] = None, + autosplit: bool = False, + split_pattern: Optional[str] = None, + loop_around_program: bool = False, + loop_and_print: bool = False, + in_place_edit: Optional[str] = None, + line_ending_processing: Optional[str] = None, + include_directory: Optional[List[Path]] = None, + use_module: Optional[List[str]] = None, + no_module: Optional[List[str]] = None, + use_module_m: Optional[List[str]] = None, + no_module_m: Optional[List[str]] = None, + enable_warnings: bool = False, + enable_all_warnings: bool = False, + disable_all_warnings: bool = False, + tainting_warnings: bool = False, + tainting_checks: bool = False, + unicode_features: Optional[str] = None, + run_under_debugger: Optional[str] = None, + debugging_flags: Optional[str] = None, + no_sitecustomize: bool = False, + parse_switches: bool = False, + search_path: bool = False, + dump_core: bool = False, + unsafe_operations: bool = False, + ignore_pre_hashbang_text: Optional[str] = None, +): + """ + Executes a script or command using the Perl interpreter. + + This tool is a wrapper for the generic 'perl' executable. The provided + documentation for 'perl-http-negotiate' indicates it is a library, not a + standalone command-line tool. This function therefore exposes the underlying + 'perl' interpreter, within an environment where 'perl-http-negotiate' is + available for use in scripts (e.g., 'use HTTP::Negotiate;'). + + Args: + programfile: Path to the Perl program file to execute. + arguments: A list of arguments to be passed to the program file. + execute_program: One or more lines of a program to be executed. Omit programfile. + execute_program_with_features: Like execute_program, but enables all optional features. + check_syntax_only: Check syntax only (runs BEGIN and CHECK blocks). Corresponds to -c. + print_version: Print version, patchlevel, and license. Corresponds to -v. + print_config: Print configuration summary. Provide a variable name to see a single value. Corresponds to -V[:variable]. + record_separator: Specify record separator (e.g., an octal number). Corresponds to -0[octal]. + autosplit: Enable autosplit mode with -n or -p (splits $_ into @F). Corresponds to -a. + split_pattern: The pattern to use for splitting in autosplit mode. Corresponds to -F/pattern/. + loop_around_program: Assume "while (<>) { ... }" loop around the program. Corresponds to -n. + loop_and_print: Assume a loop like -n but also print the line, similar to sed. Corresponds to -p. + in_place_edit: Edit <> files in place. Provide an extension to make a backup. Corresponds to -i[extension]. + line_ending_processing: Enable line ending processing and specify the line terminator. Corresponds to -l[octal]. + include_directory: Specify @INC/#include directories. Multiple paths can be provided. Corresponds to -I. + use_module: Execute "use module..." before the program. Corresponds to -Mmodule. + no_module: Execute "no module..." before the program. Corresponds to -M-module. + use_module_m: Execute "use module..." before the program. Corresponds to -mmodule. + no_module_m: Execute "no module..." before the program. Corresponds to -m-module. + enable_warnings: Enable many useful warnings. Corresponds to -w. + enable_all_warnings: Enable all warnings. Corresponds to -W. + disable_all_warnings: Disable all warnings. Corresponds to -X. + tainting_warnings: Enable tainting warnings. Corresponds to -t. + tainting_checks: Enable tainting checks. Corresponds to -T. + unicode_features: Enable the listed Unicode features. Corresponds to -C[number/list]. + run_under_debugger: Run the program under the debugger. Provide a debugger name if needed. Corresponds to -d[:debugger]. + debugging_flags: Set debugging flags (argument is a bit mask or alphabets). Corresponds to -D[number/list]. + no_sitecustomize: Don't do $sitelib/sitecustomize.pl at startup. Corresponds to -f. + parse_switches: Enable rudimentary parsing for switches after the programfile. Corresponds to -s. + search_path: Look for the programfile using the PATH environment variable. Corresponds to -S. + dump_core: Dump core after parsing the program. Corresponds to -u. + unsafe_operations: Allow unsafe operations. Corresponds to -U. + ignore_pre_hashbang_text: Ignore text before #!perl line. Optionally provide a directory to cd into. Corresponds to -x[directory]. + """ + # --- Input Validation --- + execution_target_count = sum([ + programfile is not None, + execute_program is not None, + execute_program_with_features is not None, + print_version, + print_config is not None + ]) + if execution_target_count == 0: + raise ValueError("An execution target is required. Please provide one of 'programfile', 'execute_program', 'execute_program_with_features', 'print_version', or 'print_config'.") + if execution_target_count > 1 and not (print_version and programfile): # perl -v script.pl is valid + if not (print_version and (execute_program or execute_program_with_features)): + raise ValueError("Only one primary execution target ('programfile', 'execute_program', 'execute_program_with_features', 'print_config') can be specified at a time.") + + if programfile and not programfile.exists(): + raise FileNotFoundError(f"The specified program file does not exist: {programfile}") + + # --- Command Construction --- + cmd = ["perl"] + + if record_separator is not None: cmd.append(f"-0{record_separator}") + if autosplit: cmd.append("-a") + if unicode_features is not None: cmd.append(f"-C{unicode_features}") + if check_syntax_only: cmd.append("-c") + if run_under_debugger is not None: cmd.append(f"-d{':' if run_under_debugger else ''}{run_under_debugger}") + if debugging_flags is not None: cmd.append(f"-D{debugging_flags}") + if execute_program: + for prog_line in execute_program: cmd.extend(["-e", prog_line]) + if execute_program_with_features: + for prog_line in execute_program_with_features: cmd.extend(["-E", prog_line]) + if no_sitecustomize: cmd.append("-f") + if split_pattern is not None: cmd.append(f"-F{split_pattern}") + if in_place_edit is not None: cmd.append(f"-i{in_place_edit}") + if include_directory: + for directory in include_directory: cmd.extend(["-I", str(directory)]) + if line_ending_processing is not None: cmd.append(f"-l{line_ending_processing}") + if use_module: + for module in use_module: cmd.extend(["-M", module]) + if no_module: + for module in no_module: cmd.extend(["-M-", module]) + if use_module_m: + for module in use_module_m: cmd.extend(["-m", module]) + if no_module_m: + for module in no_module_m: cmd.extend(["-m-", module]) + if loop_around_program: cmd.append("-n") + if loop_and_print: cmd.append("-p") + if parse_switches: cmd.append("-s") + if search_path: cmd.append("-S") + if tainting_warnings: cmd.append("-t") + if tainting_checks: cmd.append("-T") + if dump_core: cmd.append("-u") + if unsafe_operations: cmd.append("-U") + if print_version: cmd.append("-v") + if print_config is not None: cmd.append(f"-V{':' if print_config else ''}{print_config}") + if enable_warnings: cmd.append("-w") + if enable_all_warnings: cmd.append("-W") + if ignore_pre_hashbang_text is not None: cmd.append(f"-x{ignore_pre_hashbang_text}") + if disable_all_warnings: cmd.append("-X") + + if programfile: + cmd.append(str(programfile)) + + if arguments: + cmd.extend(arguments) + + # --- Subprocess Execution --- + command_executed = shlex.join(cmd) + try: + result = subprocess.run( + cmd, + capture_output=True, + text=True, + check=True + ) + return { + "command_executed": command_executed, + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": [] + } + except FileNotFoundError: + raise RuntimeError("The 'perl' executable was not found. Please ensure it is installed and in your system's PATH.") + except subprocess.CalledProcessError as e: + # Non-zero exit code is not necessarily an error for tools like this, + # but we return it structured for the user to decide. + return { + "command_executed": command_executed, + "stdout": e.stdout, + "stderr": e.stderr, + "error": f"Perl execution failed with return code {e.returncode}.", + "output_files": [] + } diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-http-negotiate/app/perl-http-negotiate_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-http-negotiate/app/perl-http-negotiate_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..67332377ae551a7365da845050c44291ca920241 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-http-negotiate/app/perl-http-negotiate_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-http-negotiate/app/perl-http-negotiate_server.py') +SERVER_NAME = 'biosci_perl_http_negotiate' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-http-negotiate/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-http-negotiate/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-http-negotiate/app/requirements.txt @@ -0,0 +1 @@ + diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-http-negotiate/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-http-negotiate/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..e6b9a1b636ebe48f3f2ef3bd385afaa72df6295e --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-http-negotiate/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-perl-http-negotiate: + build: . + image: mcp-perl-http-negotiate:latest + container_name: mcp-perl-http-negotiate + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=perl-http-negotiate + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-http-negotiate/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-http-negotiate/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..82c6f3445aa230c6ff9baf1e4fc14bca2456ad4b --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-http-negotiate/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - perl-http-negotiate + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-http-negotiate/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-http-negotiate/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-http-negotiate/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-image-info/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-image-info/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..01051f3910d24337130099ec8055312365ee7680 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-image-info/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install perl-image-info via conda (e.g., from bioconda) +RUN conda install -c bioconda perl-image-info -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY app/perl-image-info_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/perl-image-info_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/perl-image-info_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-image-info/app/perl-image-info_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-image-info/app/perl-image-info_server.py new file mode 100644 index 0000000000000000000000000000000000000000..64d20737776db0ff996de8982898d5ec79ed266f --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-image-info/app/perl-image-info_server.py @@ -0,0 +1,160 @@ +import subprocess +import tempfile +import json +from pathlib import Path +from typing import Dict, Any, List + +# Note: The execution of these tools requires a Perl environment with the +# Image::Info and JSON modules installed. The conda package `perl-image-info` +# provides Image::Info, and `perl-json` is a standard module usually +# available in modern Perl installations. + +@mcp.tool() +def get_image_info( + image_file: Path, +) -> Dict[str, Any]: + """ + Extracts detailed meta information from an image file using Perl's Image::Info module. + + This function wraps the `image_info` subroutine, returning a dictionary of metadata + including dimensions, color type, resolution, and format-specific information + (e.g., EXIF data for JPEGs). + + Args: + image_file: The path to the input image file. Supported formats include + AVIF, BMP, GIF, ICO, JPEG, PNG, PBM/PGM/PPM, SVG, TIFF, + WBMP, WEBP, XBM, and XPM. + + Returns: + A dictionary containing the command execution details and the extracted image metadata. + """ + if not image_file.is_file(): + raise FileNotFoundError(f"Input file not found: {image_file}") + + perl_script_content = f""" + use strict; + use warnings; + use Image::Info qw(image_info); + use JSON; + + my $file = shift @ARGV; + die "Usage: $0 \\n" unless defined $file; + + my $info = image_info($file); + + # Encode the resulting Perl hash reference to JSON for easy parsing in Python. + print encode_json($info); + """ + + with tempfile.NamedTemporaryFile(mode='w', suffix=".pl", delete=True) as tmp_script: + tmp_script.write(perl_script_content) + tmp_script.flush() + + cmd = ["perl", tmp_script.name, str(image_file)] + command_executed = " ".join(cmd) + + try: + process = subprocess.run( + cmd, + check=True, + capture_output=True, + text=True, + encoding='utf-8' + ) + + try: + metadata = json.loads(process.stdout) + except json.JSONDecodeError: + # This case handles if the script runs but outputs non-JSON text + raise ValueError(f"Failed to parse JSON output from Perl script. stdout: {process.stdout}") + + return { + "command_executed": command_executed, + "stdout": process.stdout, + "stderr": process.stderr, + "output_files": [], + "image_metadata": metadata + } + + except subprocess.CalledProcessError as e: + # This case handles if the perl script itself fails (e.g., file not found, module missing) + return { + "command_executed": command_executed, + "stdout": e.stdout, + "stderr": e.stderr, + "output_files": [], + "error": f"Perl script execution failed with return code {e.returncode}." + } + +@mcp.tool() +def get_image_type( + image_file: Path, +) -> Dict[str, Any]: + """ + Quickly determines the type of an image file using Perl's Image::Info module. + + This function wraps the `image_type` subroutine, which is a faster alternative + to get_image_info. It only identifies the file type (e.g., 'GIF', 'JPEG', 'PNG') + without parsing the full image structure. + + Args: + image_file: The path to the input image file. + + Returns: + A dictionary containing the command execution details and the determined image type. + """ + if not image_file.is_file(): + raise FileNotFoundError(f"Input file not found: {image_file}") + + perl_script_content = f""" + use strict; + use warnings; + use Image::Info qw(image_type); + use JSON; + + my $file = shift @ARGV; + die "Usage: $0 \\n" unless defined $file; + + my $type_info = image_type($file); + + # Encode the resulting Perl hash reference to JSON for easy parsing in Python. + print encode_json($type_info); + """ + + with tempfile.NamedTemporaryFile(mode='w', suffix=".pl", delete=True) as tmp_script: + tmp_script.write(perl_script_content) + tmp_script.flush() + + cmd = ["perl", tmp_script.name, str(image_file)] + command_executed = " ".join(cmd) + + try: + process = subprocess.run( + cmd, + check=True, + capture_output=True, + text=True, + encoding='utf-8' + ) + + try: + type_info = json.loads(process.stdout) + except json.JSONDecodeError: + raise ValueError(f"Failed to parse JSON output from Perl script. stdout: {process.stdout}") + + return { + "command_executed": command_executed, + "stdout": process.stdout, + "stderr": process.stderr, + "output_files": [], + "image_type_info": type_info + } + + except subprocess.CalledProcessError as e: + return { + "command_executed": command_executed, + "stdout": e.stdout, + "stderr": e.stderr, + "output_files": [], + "error": f"Perl script execution failed with return code {e.returncode}." + } \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-image-info/app/perl-image-info_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-image-info/app/perl-image-info_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..ac1b4cd13bc025414c4ea78e2c21cd3663037b6b --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-image-info/app/perl-image-info_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-image-info/app/perl-image-info_server.py') +SERVER_NAME = 'biosci_perl_image_info' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-image-info/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-image-info/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-image-info/app/requirements.txt @@ -0,0 +1 @@ + diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-image-info/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-image-info/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..8fcc40e647a8a607d4a141d9f946b8a37b541da2 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-image-info/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-perl-image-info: + build: . + image: mcp-perl-image-info:latest + container_name: mcp-perl-image-info + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=perl-image-info + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-image-info/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-image-info/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..a62f79b1dba34d53737252beba75f1a48533dda9 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-image-info/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - perl-image-info + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-image-info/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-image-info/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-image-info/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-io-string/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-io-string/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..5682fd4236f0836b450b71c7d3fb5640c9176b2c --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-io-string/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install perl-io-string via conda (e.g., from bioconda) +RUN conda install -c bioconda perl-io-string -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY app/perl-io-string_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/perl-io-string_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/perl-io-string_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-io-string/app/perl-io-string_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-io-string/app/perl-io-string_server.py new file mode 100644 index 0000000000000000000000000000000000000000..c6256d2d67b05e52cf1eb9867ec2f59d3145243b --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-io-string/app/perl-io-string_server.py @@ -0,0 +1,202 @@ +from typing import Optional, List +import subprocess +from pathlib import Path +import tempfile + +@mcp.tool() +def perl_execute( + program_code: Optional[str] = None, + program_file: Optional[str] = None, + arguments: Optional[List[str]] = None, + modules: Optional[List[str]] = ["IO::String"], + autosplit: bool = False, + split_pattern: Optional[str] = None, + syntax_check_only: bool = False, + inplace_extension: Optional[str] = None, + include_dirs: Optional[List[str]] = None, + line_ending_processing: bool = False, + loop_stdin: bool = False, + loop_stdin_print: bool = False, + warnings: bool = False, + all_warnings: bool = False, + record_separator: Optional[str] = None, + unicode_features: Optional[str] = None, +): + """ + Execute Perl code with the IO::String module available. + IO::String allows emulating a file interface for in-core strings. + + Args: + program_code: One line of program (equivalent to -e). + program_file: Path to a Perl script file to execute. + arguments: Arguments passed to the script. + modules: List of modules to execute 'use module...' before the program (equivalent to -M). Defaults to ['IO::String']. + autosplit: Enable autosplit mode with -n or -p (splits $_ into @F) (equivalent to -a). + split_pattern: split() pattern for -a switch (equivalent to -F). + syntax_check_only: Check syntax only (runs BEGIN and CHECK blocks) (equivalent to -c). + inplace_extension: Edit <> files in place, optionally making a backup with the supplied extension (equivalent to -i). + include_dirs: Specify @INC/#include directories (equivalent to -I). + line_ending_processing: Enable line ending processing, specifies line terminator (equivalent to -l). + loop_stdin: Assume "while (<>) { ... }" loop around program (equivalent to -n). + loop_stdin_print: Assume loop like -n but print line also (equivalent to -p). + warnings: Enable many useful warnings (equivalent to -w). + all_warnings: Enable all warnings (equivalent to -W). + record_separator: Specify record separator (equivalent to -0). + unicode_features: Enables the listed Unicode features (equivalent to -C). + """ + + command = ["perl"] + + # Handle switches + if record_separator is not None: + command.append(f"-0{record_separator}") + + if autosplit: + command.append("-a") + + if unicode_features: + command.append(f"-C{unicode_features}") + + if syntax_check_only: + command.append("-c") + + if split_pattern: + command.append(f"-F{split_pattern}") + + if inplace_extension is not None: + command.append(f"-i{inplace_extension}") + + if include_dirs: + for d in include_dirs: + command.append(f"-I{d}") + + if line_ending_processing: + command.append("-l") + + if modules: + for m in modules: + command.append(f"-M{m}") + + if loop_stdin: + command.append("-n") + + if loop_stdin_print: + command.append("-p") + + if warnings: + command.append("-w") + + if all_warnings: + command.append("-W") + + # Handle program input + if program_code and program_file: + raise ValueError("Cannot provide both program_code and program_file.") + + if program_code: + command.extend(["-e", program_code]) + elif program_file: + p_file = Path(program_file) + if not p_file.exists(): + return {"error": f"Program file not found: {program_file}"} + command.append(str(p_file)) + else: + # If neither is provided, we might be expecting code via stdin or just checking version + # But for this tool, we'll assume at least one is needed unless it's a version check + pass + + # Append arguments + if arguments: + command.extend(arguments) + + try: + result = subprocess.run( + command, + capture_output=True, + text=True, + check=True + ) + return { + "command_executed": " ".join(command), + "stdout": result.stdout, + "stderr": result.stderr, + "status": "success" + } + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(command), + "stdout": e.stdout, + "stderr": e.stderr, + "error": str(e), + "status": "error" + } + +@mcp.tool() +def perl_version(): + """ + Print the version, patchlevel, and license of the Perl interpreter. + """ + command = ["perl", "-v"] + try: + result = subprocess.run(command, capture_output=True, text=True, check=True) + return { + "command_executed": " ".join(command), + "stdout": result.stdout, + "stderr": result.stderr + } + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(command), + "stdout": e.stdout, + "stderr": e.stderr, + "error": str(e) + } + +@mcp.tool() +def perl_config_summary(variable: Optional[str] = None): + """ + Print a configuration summary for the Perl interpreter or a single Config.pm variable. + + Args: + variable: A specific Config.pm variable to query (e.g., 'archname'). + """ + command = ["perl"] + if variable: + command.append(f"-V:{variable}") + else: + command.append("-V") + + try: + result = subprocess.run(command, capture_output=True, text=True, check=True) + return { + "command_executed": " ".join(command), + "stdout": result.stdout, + "stderr": result.stderr + } + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(command), + "stdout": e.stdout, + "stderr": e.stderr, + "error": str(e) + } + +@mcp.tool() +def perl_io_string_example(): + """ + Returns a sample Perl snippet demonstrating how to use IO::String to treat a string as a file. + This is useful for users unfamiliar with the module's syntax. + """ + example_code = """ +use IO::String; +my $data = "Hello\\nWorld\\nBioinformatics"; +my $io = IO::String->new($data); +while (<$io>) { + print "Line $.: $_"; +} +""" + return { + "example_description": "Reading from a string as if it were a file handle.", + "code": example_code, + "usage_instruction": "Pass this code to the perl_execute tool's program_code parameter." + } \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-io-string/app/perl-io-string_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-io-string/app/perl-io-string_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..000c10e972b8f4e48478174d7a298ffadbc79148 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-io-string/app/perl-io-string_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-io-string/app/perl-io-string_server.py') +SERVER_NAME = 'biosci_perl_io_string' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-io-string/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-io-string/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-io-string/app/requirements.txt @@ -0,0 +1 @@ + diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-io-string/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-io-string/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..e4f835cf8ef424fa0068cc0afb649fcfe5f8adc2 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-io-string/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-perl-io-string: + build: . + image: mcp-perl-io-string:latest + container_name: mcp-perl-io-string + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=perl-io-string + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-io-string/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-io-string/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..dea1437faea88d9daca340a235c91faf140f6a7c --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-io-string/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - perl-io-string + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-io-string/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-io-string/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-io-string/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-json-pp/app/perl-json-pp_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-json-pp/app/perl-json-pp_server.py new file mode 100644 index 0000000000000000000000000000000000000000..e7fa86cbc831c24c7601a84e46a0fe9042712768 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-json-pp/app/perl-json-pp_server.py @@ -0,0 +1,180 @@ +import subprocess +from pathlib import Path +from typing import Optional, List + +@mcp.tool() +def json_pp( + input_file: str, + output_file: Optional[str] = None, + from_format: str = "json", + to_format: str = "json", + pretty: bool = True, + ascii: bool = False, + latin1: bool = False, + utf8: bool = True, + indent: int = 2, + space_before: bool = False, + space_after: bool = True, + relaxed: bool = False, + canonical: bool = False, + allow_nonref: bool = True, + allow_unknown: bool = False, + allow_blessed: bool = False, + convert_blessed: bool = False, + verbose: bool = False +): + """ + Converts between different JSON formats using the perl-json-pp utility. + Supports reading from JSON, Perl Eval, or YAML and outputting to JSON or Perl Eval. + + :param input_file: Path to the input file to process. + :param output_file: Path to save the output. If not provided, output is returned in stdout. + :param from_format: Input format. Options: 'json', 'eval', 'yaml'. Default: 'json'. + :param to_format: Output format. Options: 'json', 'eval'. Default: 'json'. + :param pretty: Enables pretty-printing of the output. + :param ascii: Use ASCII only; escape all non-ASCII characters. + :param latin1: Encode output in Latin-1. + :param utf8: Encode output in UTF-8. + :param indent: Number of spaces for indentation (used if pretty is True). + :param space_before: Add a space before the colon in key-value pairs. + :param space_after: Add a space after the colon in key-value pairs. + :param relaxed: Allow relaxed JSON syntax (e.g., trailing commas). + :param canonical: Sort object keys alphabetically. + :param allow_nonref: Allow non-reference values (scalars) as top-level JSON. + :param allow_unknown: Allow unknown types to be encoded as null. + :param allow_blessed: Allow blessed references (objects). + :param convert_blessed: Use TO_JSON method on blessed objects if available. + :param verbose: Enable verbose output. + """ + # Input validation + input_path = Path(input_file) + if not input_path.is_file(): + return {"error": f"Input file not found: {input_file}"} + + valid_from = ["json", "eval", "yaml"] + valid_to = ["json", "eval"] + + if from_format not in valid_from: + return {"error": f"Invalid from_format '{from_format}'. Must be one of {valid_from}"} + if to_format not in valid_to: + return {"error": f"Invalid to_format '{to_format}'. Must be one of {valid_to}"} + + if indent < 0: + return {"error": "Indent must be a non-negative integer."} + + # Construct command + cmd = ["json_pp"] + + # Format flags + cmd.extend(["-f", from_format]) + cmd.extend(["-t", to_format]) + + if verbose: + cmd.append("-v") + + # JSON options (-json_opt) + json_opts = [] + if pretty: + json_opts.append("pretty") + if ascii: + json_opts.append("ascii") + if latin1: + json_opts.append("latin1") + if utf8: + json_opts.append("utf8") + if indent != 2: + json_opts.append(f"indent:{indent}") + if space_before: + json_opts.append("space_before") + if space_after: + json_opts.append("space_after") + if relaxed: + json_opts.append("relaxed") + if canonical: + json_opts.append("canonical") + if allow_nonref: + json_opts.append("allow_nonref") + if allow_unknown: + json_opts.append("allow_unknown") + if allow_blessed: + json_opts.append("allow_blessed") + if convert_blessed: + json_opts.append("convert_blessed") + + if json_opts: + cmd.extend(["-json_opt", ",".join(json_opts)]) + + # Execution + try: + # json_pp reads from STDIN + with open(input_path, "r") as f_in: + if output_file: + output_path = Path(output_file) + with open(output_path, "w") as f_out: + process = subprocess.run( + cmd, + stdin=f_in, + stdout=f_out, + stderr=subprocess.PIPE, + text=True, + check=True + ) + stdout_content = f"Output written to {output_file}" + output_files = [str(output_path.absolute())] + else: + process = subprocess.run( + cmd, + stdin=f_in, + capture_output=True, + text=True, + check=True + ) + stdout_content = process.stdout + output_files = [] + + return { + "command_executed": " ".join(cmd), + "stdout": stdout_content, + "stderr": process.stderr, + "output_files": output_files + } + + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(cmd), + "error": "Execution failed", + "stdout": e.stdout, + "stderr": e.stderr, + "return_code": e.returncode + } + except Exception as e: + return { + "command_executed": " ".join(cmd), + "error": str(e) + } + +@mcp.tool() +def json_pp_version(): + """ + Returns the version information for the installed json_pp utility. + """ + cmd = ["json_pp", "-v"] + try: + # json_pp -v usually prints version and exits, but might expect STDIN + # We provide empty input to prevent hanging + process = subprocess.run( + cmd, + input="", + capture_output=True, + text=True, + check=False # -v might return non-zero in some versions if no input + ) + return { + "command_executed": " ".join(cmd), + "stdout": process.stdout, + "stderr": process.stderr + } + except Exception as e: + return { + "error": f"Could not determine version: {str(e)}" + } \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-json-pp/app/perl-json-pp_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-json-pp/app/perl-json-pp_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..ab8873711a27f437ac3391036c1830af4ac32d10 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-json-pp/app/perl-json-pp_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-json-pp/app/perl-json-pp_server.py') +SERVER_NAME = 'biosci_perl_json_pp' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-json-pp/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-json-pp/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..07d786bf4ac683261dd729143f5f388d71be3618 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-json-pp/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-perl-json-pp: + build: . + image: mcp-perl-json-pp:latest + container_name: mcp-perl-json-pp + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=perl-json-pp + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-json-pp/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-json-pp/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..f65fd8e82f6643a171ed05900e531c668e9541a6 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-json-pp/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - perl-json-pp + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-json-pp/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-json-pp/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-json-pp/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-libxml-perl/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-libxml-perl/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..08b89036ef919de80b6e4ad1450f549f803d6b21 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-libxml-perl/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install perl-libxml-perl via conda (e.g., from bioconda) +RUN conda install -c bioconda perl-libxml-perl -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY app/perl-libxml-perl_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/perl-libxml-perl_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/perl-libxml-perl_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-libxml-perl/app/perl-libxml-perl_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-libxml-perl/app/perl-libxml-perl_server.py new file mode 100644 index 0000000000000000000000000000000000000000..2c389b5c1a8ae6912739f66e85a10abc791fcee7 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-libxml-perl/app/perl-libxml-perl_server.py @@ -0,0 +1,180 @@ +import subprocess +from pathlib import Path +from typing import Optional, List +import logging + +# Set up logging +logging.basicConfig(level=logging.INFO) +logger = logging.getLogger(__name__) + +# @mcp.tool() +def perl_interpreter( + programfile: Optional[Path] = None, + arguments: Optional[List[str]] = None, + execute_program: Optional[List[str]] = None, + execute_program_extended: Optional[List[str]] = None, + check_syntax_only: bool = False, + run_debugger: bool = False, + debugger_module: Optional[str] = None, + debugging_flags: Optional[str] = None, + enable_warnings: bool = False, + enable_all_warnings: bool = False, + disable_all_warnings: bool = False, + taint_warnings: bool = False, + taint_checks: bool = False, + include_dirs: Optional[List[Path]] = None, + use_modules: Optional[List[str]] = None, + use_modules_no_import: Optional[List[str]] = None, + no_modules: Optional[List[str]] = None, + loop_program: bool = False, + loop_and_print: bool = False, + autosplit: bool = False, + split_pattern: Optional[str] = None, + in_place_edit: bool = False, + backup_extension: Optional[str] = None, + line_ending_processing: Optional[str] = None, + record_separator: Optional[str] = None, + parse_switches: bool = False, + search_path: bool = False, + dump_core: bool = False, + allow_unsafe: bool = False, + no_sitecustomize: bool = False, + unicode_features: Optional[str] = None, + extract_script: bool = False, + change_dir_before_extract: Optional[Path] = None, + print_version: bool = False, + print_config: bool = False, + config_variable: Optional[str] = None, +): + """ + Executes scripts using the Perl interpreter. + + This tool is a comprehensive wrapper for the `perl` command-line executable. + It allows for running Perl scripts from files or directly as one-liners, + and provides access to a wide range of Perl's execution switches for + debugging, module loading, and input/output processing. + """ + # --- Input Validation --- + if not any([programfile, execute_program, execute_program_extended, print_version, print_config]): + raise ValueError("Either a program file, a program string (-e or -E), or an informational flag (-v or -V) must be provided.") + + if programfile and not programfile.is_file(): + raise FileNotFoundError(f"Program file not found: {programfile}") + + if loop_program and loop_and_print: + raise ValueError("Cannot use -n (loop_program) and -p (loop_and_print) simultaneously.") + + if enable_all_warnings and disable_all_warnings: + raise ValueError("Cannot use -W (enable_all_warnings) and -X (disable_all_warnings) simultaneously.") + + if backup_extension is not None and not in_place_edit: + raise ValueError("backup_extension (-i) requires in_place_edit (-i) to be True.") + + if config_variable and not print_config: + raise ValueError("config_variable (-V:variable) requires print_config (-V) to be True.") + + if debugger_module and not run_debugger: + raise ValueError("debugger_module (-d:debugger) requires run_debugger (-d) to be True.") + + if change_dir_before_extract and not extract_script: + raise ValueError("change_dir_before_extract (-x directory) requires extract_script (-x) to be True.") + + # --- Command Construction --- + cmd = ["perl"] + + if record_separator is not None: cmd.append(f"-0{record_separator}") + if autosplit: cmd.append("-a") + if unicode_features is not None: cmd.append(f"-C{unicode_features}") + if check_syntax_only: cmd.append("-c") + if run_debugger: + debug_flag = "-d" + if debugger_module: debug_flag += f":{debugger_module}" + cmd.append(debug_flag) + if debugging_flags is not None: cmd.append(f"-D{debugging_flags}") + if execute_program: + for prog in execute_program: cmd.extend(["-e", prog]) + if execute_program_extended: + for prog in execute_program_extended: cmd.extend(["-E", prog]) + if no_sitecustomize: cmd.append("-f") + if split_pattern is not None: cmd.append(f"-F{split_pattern}") + if in_place_edit: + i_flag = "-i" + if backup_extension is not None: i_flag += backup_extension + cmd.append(i_flag) + if include_dirs: + for directory in include_dirs: cmd.extend(["-I", str(directory)]) + if line_ending_processing is not None: cmd.append(f"-l{line_ending_processing}") + if use_modules: + for module in use_modules: cmd.append(f"-M{module}") + if use_modules_no_import: + for module in use_modules_no_import: cmd.append(f"-m{module}") + if no_modules: + for module in no_modules: cmd.append(f"-M-{module}") + if loop_program: cmd.append("-n") + if loop_and_print: cmd.append("-p") + if parse_switches: cmd.append("-s") + if search_path: cmd.append("-S") + if taint_warnings: cmd.append("-t") + if taint_checks: cmd.append("-T") + if dump_core: cmd.append("-u") + if allow_unsafe: cmd.append("-U") + if print_version: cmd.append("-v") + if print_config: + v_flag = "-V" + if config_variable: v_flag += f":{config_variable}" + cmd.append(v_flag) + if enable_warnings: cmd.append("-w") + if enable_all_warnings: cmd.append("-W") + if extract_script: + x_flag = "-x" + if change_dir_before_extract: x_flag += str(change_dir_before_extract) + cmd.append(x_flag) + if disable_all_warnings: cmd.append("-X") + + if programfile: + cmd.append(str(programfile)) + if arguments: + cmd.extend(arguments) + + # --- Subprocess Execution --- + command_executed = " ".join(cmd) + logger.info(f"Executing command: {command_executed}") + + try: + result = subprocess.run( + cmd, + capture_output=True, + text=True, + check=False, # Set to False to handle non-zero exit codes manually + ) + + if result.returncode != 0: + logger.error(f"Command failed with exit code {result.returncode}") + logger.error(f"Stderr: {result.stderr}") + + return { + "command_executed": command_executed, + "stdout": result.stdout, + "stderr": result.stderr, + "return_code": result.returncode, + "output_files": [] + } + except FileNotFoundError: + stderr_msg = "Error: 'perl' command not found. Please ensure Perl is installed and in the system's PATH." + logger.error(stderr_msg) + return { + "command_executed": command_executed, + "stdout": "", + "stderr": stderr_msg, + "return_code": 1, + "output_files": [] + } + except Exception as e: + logger.error(f"An unexpected error occurred: {e}") + return { + "command_executed": command_executed, + "stdout": "", + "stderr": str(e), + "return_code": 1, + "output_files": [] + } \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-libxml-perl/app/perl-libxml-perl_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-libxml-perl/app/perl-libxml-perl_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..c90582dfa6c66a4f8c4fe9c8be7c724d4f9df9cb --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-libxml-perl/app/perl-libxml-perl_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-libxml-perl/app/perl-libxml-perl_server.py') +SERVER_NAME = 'biosci_perl_libxml_perl' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-libxml-perl/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-libxml-perl/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-libxml-perl/app/requirements.txt @@ -0,0 +1 @@ + diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-libxml-perl/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-libxml-perl/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..2f296c50f5a1823252191716f4a5a2b092641e02 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-libxml-perl/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-perl-libxml-perl: + build: . + image: mcp-perl-libxml-perl:latest + container_name: mcp-perl-libxml-perl + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=perl-libxml-perl + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-libxml-perl/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-libxml-perl/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..e1c8320a14489a7a62de9bb968e7ae5ab2037e9a --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-libxml-perl/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - perl-libxml-perl + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-libxml-perl/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-libxml-perl/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-libxml-perl/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-list-moreutils/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-list-moreutils/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..7fe2222d2c3b01f8391ccda2a0d690d823d472da --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-list-moreutils/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install perl-list-moreutils via conda (e.g., from bioconda) +RUN conda install -c bioconda perl-list-moreutils -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY app/perl-list-moreutils_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/perl-list-moreutils_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/perl-list-moreutils_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-list-moreutils/app/perl-list-moreutils_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-list-moreutils/app/perl-list-moreutils_server.py new file mode 100644 index 0000000000000000000000000000000000000000..aec81934e0ab77c19e925d705cae5189ec50beb1 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-list-moreutils/app/perl-list-moreutils_server.py @@ -0,0 +1,344 @@ +import subprocess +from pathlib import Path +from typing import Optional, List, Union +import tempfile + +def _run_perl_command(script: str, input_data: Optional[str] = None, input_file: Optional[str] = None): + """ + Internal helper to execute Perl one-liners using List::MoreUtils. + """ + cmd = ["perl", "-MList::MoreUtils=:all", "-e", script] + + stdin_handle = None + if input_file: + input_path = Path(input_file) + if not input_path.exists(): + raise FileNotFoundError(f"Input file not found: {input_file}") + stdin_handle = open(input_path, "r") + + try: + process = subprocess.run( + cmd, + input=input_data if not input_file else None, + stdin=stdin_handle, + capture_output=True, + text=True, + check=True + ) + return { + "command_executed": " ".join(cmd), + "stdout": process.stdout, + "stderr": process.stderr, + "return_code": process.returncode + } + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(cmd), + "stdout": e.stdout, + "stderr": e.stderr, + "return_code": e.returncode, + "error": str(e) + } + finally: + if stdin_handle: + stdin_handle.close() + +@mcp.tool() +def list_moreutils_uniq( + input_file: str, + output_file: Optional[str] = None, +): + """ + Returns a list of the elements, in the order they first appear, with all duplicates removed. + + Args: + input_file: Path to the input file (one element per line). + output_file: Optional path to save the unique elements. + """ + input_path = Path(input_file) + if not input_path.exists(): + return {"error": f"Input file {input_file} does not exist."} + + # Perl script to read lines, chomp them, run uniq, and print + script = 'print uniq();' + + result = _run_perl_command(script, input_file=input_file) + + if output_file and "stdout" in result: + out_path = Path(output_file) + out_path.write_text(result["stdout"]) + result["output_files"] = [str(out_path)] + + return result + +@mcp.tool() +def list_moreutils_singleton( + input_file: str, + output_file: Optional[str] = None, +): + """ + Returns a list of the elements which appear exactly once in the input. + + Args: + input_file: Path to the input file (one element per line). + output_file: Optional path to save the singleton elements. + """ + input_path = Path(input_file) + if not input_path.exists(): + return {"error": f"Input file {input_file} does not exist."} + + script = 'print singleton();' + result = _run_perl_command(script, input_file=input_file) + + if output_file and "stdout" in result: + out_path = Path(output_file) + out_path.write_text(result["stdout"]) + result["output_files"] = [str(out_path)] + + return result + +@mcp.tool() +def list_moreutils_mesh( + file1: str, + file2: str, + output_file: Optional[str] = None, +): + """ + Interleaves the elements of two files. Also known as 'zip'. + + Args: + file1: Path to the first input file. + file2: Path to the second input file. + output_file: Optional path to save the meshed output. + """ + p1 = Path(file1) + p2 = Path(file2) + if not p1.exists() or not p2.exists(): + return {"error": "One or both input files do not exist."} + + # Read both files into arrays and mesh them + script = ( + 'open my $f1, "<", $ARGV[0] or die; my @a = <$f1>; ' + 'open my $f2, "<", $ARGV[1] or die; my @b = <$f2>; ' + 'print mesh @a, @b;' + ) + + cmd = ["perl", "-MList::MoreUtils=:all", "-e", script, str(p1), str(p2)] + + try: + process = subprocess.run(cmd, capture_output=True, text=True, check=True) + result = { + "command_executed": " ".join(cmd), + "stdout": process.stdout, + "stderr": process.stderr + } + if output_file: + out_path = Path(output_file) + out_path.write_text(process.stdout) + result["output_files"] = [str(out_path)] + return result + except subprocess.CalledProcessError as e: + return {"error": str(e), "stderr": e.stderr} + +@mcp.tool() +def list_moreutils_minmax( + input_file: str, +): + """ + Calculates the minimum and maximum values in a list of numbers. + + Args: + input_file: Path to a file containing one number per line. + """ + input_path = Path(input_file) + if not input_path.exists(): + return {"error": f"Input file {input_file} does not exist."} + + script = 'my @a = ; my ($min, $max) = minmax @a; print "Min: $min\nMax: $max\n";' + return _run_perl_command(script, input_file=input_file) + +@mcp.tool() +def list_moreutils_junction( + input_file: str, + mode: str = "any", + pattern: str = ".", +): + """ + Performs junction logic (any, all, none, notall) on a list based on a regex pattern. + + Args: + input_file: Path to the input file. + mode: Logic mode to use: 'any', 'all', 'none', or 'notall'. + pattern: Regex pattern to match against each line. + """ + if mode not in ["any", "all", "none", "notall"]: + return {"error": "Mode must be one of: any, all, none, notall"} + + input_path = Path(input_file) + if not input_path.exists(): + return {"error": f"Input file {input_file} does not exist."} + + # Escape pattern for Perl + safe_pattern = pattern.replace("'", "\\'") + script = f'my @a = ; if ({mode} {{ m/{safe_pattern}/ }} @a) {{ print "True\\n" }} else {{ print "False\\n" }}' + + return _run_perl_command(script, input_file=input_file) + +@mcp.tool() +def list_moreutils_counting( + input_file: str, + mode: str = "true", + pattern: str = ".", +): + """ + Counts how many elements match (true) or do not match (false) a pattern. + + Args: + input_file: Path to the input file. + mode: 'true' to count matches, 'false' to count non-matches. + pattern: Regex pattern to match. + """ + if mode not in ["true", "false"]: + return {"error": "Mode must be 'true' or 'false'"} + + input_path = Path(input_file) + if not input_path.exists(): + return {"error": f"Input file {input_file} does not exist."} + + safe_pattern = pattern.replace("'", "\\'") + script = f'my @a = ; print {mode} {{ m/{safe_pattern}/ }} @a;' + + return _run_perl_command(script, input_file=input_file) + +@mcp.tool() +def list_moreutils_filtering( + input_file: str, + mode: str = "before", + pattern: str = ".", + include_boundary: bool = False, + output_file: Optional[str] = None, +): + """ + Filters a list based on a boundary pattern. + + Args: + input_file: Path to the input file. + mode: 'before' (elements before match) or 'after' (elements after match). + pattern: Regex pattern defining the boundary. + include_boundary: Whether to include the element that matched the pattern. + output_file: Optional path to save results. + """ + if mode not in ["before", "after"]: + return {"error": "Mode must be 'before' or 'after'"} + + input_path = Path(input_file) + if not input_path.exists(): + return {"error": f"Input file {input_file} does not exist."} + + func = mode + if include_boundary: + func += "_incl" + + safe_pattern = pattern.replace("'", "\\'") + script = f'my @a = ; print {func} {{ m/{safe_pattern}/ }} @a;' + + result = _run_perl_command(script, input_file=input_file) + + if output_file and "stdout" in result: + out_path = Path(output_file) + out_path.write_text(result["stdout"]) + result["output_files"] = [str(out_path)] + + return result + +@mcp.tool() +def list_moreutils_apply( + input_file: str, + perl_expression: str = "s/\\s+//g", + output_file: Optional[str] = None, +): + """ + Applies a Perl transformation to every element in the list. + + Args: + input_file: Path to the input file. + perl_expression: Perl code to apply to $_ (e.g., 's/foo/bar/' or '$_ = reverse'). + output_file: Optional path to save results. + """ + input_path = Path(input_file) + if not input_path.exists(): + return {"error": f"Input file {input_file} does not exist."} + + # apply { ... } @list returns the modified list + script = f'my @a = ; print apply {{ {perl_expression} }} @a;' + + result = _run_perl_command(script, input_file=input_file) + + if output_file and "stdout" in result: + out_path = Path(output_file) + out_path.write_text(result["stdout"]) + result["output_files"] = [str(out_path)] + + return result + +@mcp.tool() +def list_moreutils_indexes( + input_file: str, + pattern: str = ".", +): + """ + Returns the 0-based indices of all elements matching the pattern. + + Args: + input_file: Path to the input file. + pattern: Regex pattern to match. + """ + input_path = Path(input_file) + if not input_path.exists(): + return {"error": f"Input file {input_file} does not exist."} + + safe_pattern = pattern.replace("'", "\\'") + script = f'my @a = ; print join("\\n", indexes {{ m/{safe_pattern}/ }} @a);' + + return _run_perl_command(script, input_file=input_file) + +@mcp.tool() +def list_moreutils_part( + input_file: str, + perl_condition: str = "$_ =~ /pattern/ ? 0 : 1", + output_prefix: str = "part", +): + """ + Partitions a list into multiple files based on a Perl condition. + The condition should return an integer index (0, 1, 2...). + + Args: + input_file: Path to the input file. + perl_condition: Perl expression returning an index for each element. + output_prefix: Prefix for the generated partition files (e.g., part_0.txt, part_1.txt). + """ + input_path = Path(input_file) + if not input_path.exists(): + return {"error": f"Input file {input_file} does not exist."} + + # This script partitions the list and writes each part to a separate file + script = ( + f'my @a = ; ' + f'my @parts = part {{ {perl_condition} }} @a; ' + f'for my $i (0 .. $#parts) {{ ' + f' if ($parts[$i]) {{ ' + f' open my $fh, ">", "{output_prefix}_$i.txt" or die; ' + f' print $fh @{{$parts[$i]}}; ' + f' close $fh; ' + f' print "{output_prefix}_$i.txt\\n"; ' + f' }} ' + f'}}' + ) + + result = _run_perl_command(script, input_file=input_file) + + if "stdout" in result: + files = result["stdout"].strip().split("\n") + result["output_files"] = [f for f in files if f] + + return result \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-list-moreutils/app/perl-list-moreutils_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-list-moreutils/app/perl-list-moreutils_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..0b13db3129f0a7638fbb35fa2412350851ea0d63 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-list-moreutils/app/perl-list-moreutils_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-list-moreutils/app/perl-list-moreutils_server.py') +SERVER_NAME = 'biosci_perl_list_moreutils' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-list-moreutils/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-list-moreutils/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-list-moreutils/app/requirements.txt @@ -0,0 +1 @@ + diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-list-moreutils/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-list-moreutils/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..810993544ab20ad8894bbd61b484efed9c376fbd --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-list-moreutils/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-perl-list-moreutils: + build: . + image: mcp-perl-list-moreutils:latest + container_name: mcp-perl-list-moreutils + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=perl-list-moreutils + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-list-moreutils/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-list-moreutils/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..ca225013df9e37225c0933287c09b4949b1eac45 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-list-moreutils/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - perl-list-moreutils + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-list-moreutils/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-list-moreutils/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-list-moreutils/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-module-corelist/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-module-corelist/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..cb9f25b0f4529b2a654370ff1da38faab3ab983f --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-module-corelist/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install perl-module-corelist via conda (e.g., from bioconda) +RUN conda install -c bioconda perl-module-corelist -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY app/perl-module-corelist_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/perl-module-corelist_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/perl-module-corelist_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-module-corelist/app/perl-module-corelist_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-module-corelist/app/perl-module-corelist_server.py new file mode 100644 index 0000000000000000000000000000000000000000..de138f1fa04dbca2bfaf9123cdbeadec2f66d370 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-module-corelist/app/perl-module-corelist_server.py @@ -0,0 +1,360 @@ +import subprocess +import sys +from pathlib import Path +from typing import List, Optional + +# Note: The provided help was for the 'perl' interpreter. +# The actual tool is 'corelist', and its options have been sourced +# from standard documentation for the Module::CoreList package. + + +@mcp.tool() +def query_module( + module_name: str, + list_all_versions: bool = False +): + """ + Finds in which version(s) of perl a given module was shipped in the core distribution. + + Args: + module_name: The name of the Perl module to query (e.g., 'Test::More'). + list_all_versions: If True, lists all versions of perl that shipped with the module. + """ + if not module_name: + raise ValueError("module_name cannot be empty.") + + cmd = ["corelist"] + if list_all_versions: + cmd.append("-a") + cmd.append(module_name) + + try: + result = subprocess.run( + cmd, + capture_output=True, + text=True, + check=True + ) + return { + "command_executed": " ".join(cmd), + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": [] + } + except FileNotFoundError: + return { + "command_executed": " ".join(cmd), + "stdout": "", + "stderr": "Error: 'corelist' command not found. Please ensure the perl-module-corelist package is installed and in your PATH.", + "output_files": [] + } + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(cmd), + "stdout": e.stdout, + "stderr": e.stderr, + "output_files": [] + } + + +@mcp.tool() +def query_perl_version( + perl_version: str, + list_deprecated: bool = False +): + """ + Lists all modules or only deprecated modules shipped with a specific version of perl. + + Args: + perl_version: The specific version of perl to query (e.g., '5.014002'). + list_deprecated: If True, lists only modules that were deprecated in this perl version. + """ + if not perl_version: + raise ValueError("perl_version cannot be empty.") + + cmd = ["corelist"] + if list_deprecated: + cmd.append("-d") + else: + cmd.append("-v") + cmd.append(perl_version) + + try: + result = subprocess.run( + cmd, + capture_output=True, + text=True, + check=True + ) + return { + "command_executed": " ".join(cmd), + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": [] + } + except FileNotFoundError: + return { + "command_executed": " ".join(cmd), + "stdout": "", + "stderr": "Error: 'corelist' command not found. Please ensure the perl-module-corelist package is installed and in your PATH.", + "output_files": [] + } + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(cmd), + "stdout": e.stdout, + "stderr": e.stderr, + "output_files": [] + } + + +@mcp.tool() +def compare_perl_versions( + perl_version1: str, + perl_version2: str, + output_format: str = "text", + diff_type: str = "modules" +): + """ + Shows the differences in modules between two perl versions. + + Args: + perl_version1: The first perl version for comparison. + perl_version2: The second perl version for comparison. + output_format: The format for the output ('text' or 'json'). JSON is only for 'modules' diff_type. + diff_type: The type of difference to show ('modules', 'pre-release', 'deprecated'). + """ + if not perl_version1 or not perl_version2: + raise ValueError("perl_version1 and perl_version2 cannot be empty.") + + allowed_formats = ["text", "json"] + if output_format not in allowed_formats: + raise ValueError(f"Invalid output_format '{output_format}'. Allowed values are: {allowed_formats}") + + allowed_diff_types = ["modules", "pre-release", "deprecated"] + if diff_type not in allowed_diff_types: + raise ValueError(f"Invalid diff_type '{diff_type}'. Allowed values are: {allowed_diff_types}") + + if output_format == "json" and diff_type != "modules": + raise ValueError("JSON output format is only available for diff_type 'modules'.") + + cmd = ["corelist"] + flag_map = { + ("text", "modules"): "--diff", + ("json", "modules"): "--json-diff", + ("text", "pre-release"): "--diff-pre", + ("text", "deprecated"): "--diff-deprecated" + } + flag = flag_map[(output_format, diff_type)] + cmd.extend([flag, perl_version1, perl_version2]) + + try: + result = subprocess.run( + cmd, + capture_output=True, + text=True, + check=True + ) + return { + "command_executed": " ".join(cmd), + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": [] + } + except FileNotFoundError: + return { + "command_executed": " ".join(cmd), + "stdout": "", + "stderr": "Error: 'corelist' command not found. Please ensure the perl-module-corelist package is installed and in your PATH.", + "output_files": [] + } + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(cmd), + "stdout": e.stdout, + "stderr": e.stderr, + "output_files": [] + } + + +@mcp.tool() +def list_perl_versions(): + """ + Lists all perl versions for which Module::CoreList has data. + """ + cmd = ["corelist", "--list-perl"] + try: + result = subprocess.run( + cmd, + capture_output=True, + text=True, + check=True + ) + return { + "command_executed": " ".join(cmd), + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": [] + } + except FileNotFoundError: + return { + "command_executed": " ".join(cmd), + "stdout": "", + "stderr": "Error: 'corelist' command not found. Please ensure the perl-module-corelist package is installed and in your PATH.", + "output_files": [] + } + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(cmd), + "stdout": e.stdout, + "stderr": e.stderr, + "output_files": [] + } + + +@mcp.tool() +def list_all_modules(): + """ + Lists all modules for which Module::CoreList has data. + """ + cmd = ["corelist", "--list-modules"] + try: + result = subprocess.run( + cmd, + capture_output=True, + text=True, + check=True + ) + return { + "command_executed": " ".join(cmd), + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": [] + } + except FileNotFoundError: + return { + "command_executed": " ".join(cmd), + "stdout": "", + "stderr": "Error: 'corelist' command not found. Please ensure the perl-module-corelist package is installed and in your PATH.", + "output_files": [] + } + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(cmd), + "stdout": e.stdout, + "stderr": e.stderr, + "output_files": [] + } + + +@mcp.tool() +def list_all_deprecated_modules(): + """ + Lists all deprecated modules for which Module::CoreList has data. + """ + cmd = ["corelist", "--list-deprecated"] + try: + result = subprocess.run( + cmd, + capture_output=True, + text=True, + check=True + ) + return { + "command_executed": " ".join(cmd), + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": [] + } + except FileNotFoundError: + return { + "command_executed": " ".join(cmd), + "stdout": "", + "stderr": "Error: 'corelist' command not found. Please ensure the perl-module-corelist package is installed and in your PATH.", + "output_files": [] + } + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(cmd), + "stdout": e.stdout, + "stderr": e.stderr, + "output_files": [] + } + + +@mcp.tool() +def query_feature( + feature_name: str +): + """ + Shows in which perl version a given feature (e.g., 'say') appeared. + + Args: + feature_name: The name of the feature to query. + """ + if not feature_name: + raise ValueError("feature_name cannot be empty.") + + cmd = ["corelist", "--feature", feature_name] + try: + result = subprocess.run( + cmd, + capture_output=True, + text=True, + check=True + ) + return { + "command_executed": " ".join(cmd), + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": [] + } + except FileNotFoundError: + return { + "command_executed": " ".join(cmd), + "stdout": "", + "stderr": "Error: 'corelist' command not found. Please ensure the perl-module-corelist package is installed and in your PATH.", + "output_files": [] + } + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(cmd), + "stdout": e.stdout, + "stderr": e.stderr, + "output_files": [] + } + + +@mcp.tool() +def get_version(): + """ + Shows the version of the Module::CoreList data. + """ + cmd = ["corelist", "--version"] + try: + result = subprocess.run( + cmd, + capture_output=True, + text=True, + check=True + ) + return { + "command_executed": " ".join(cmd), + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": [] + } + except FileNotFoundError: + return { + "command_executed": " ".join(cmd), + "stdout": "", + "stderr": "Error: 'corelist' command not found. Please ensure the perl-module-corelist package is installed and in your PATH.", + "output_files": [] + } + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(cmd), + "stdout": e.stdout, + "stderr": e.stderr, + "output_files": [] + } \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-module-corelist/app/perl-module-corelist_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-module-corelist/app/perl-module-corelist_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..2ca626ddbc6224c444280ed89a0779f548876452 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-module-corelist/app/perl-module-corelist_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-module-corelist/app/perl-module-corelist_server.py') +SERVER_NAME = 'biosci_perl_module_corelist' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-module-corelist/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-module-corelist/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-module-corelist/app/requirements.txt @@ -0,0 +1 @@ + diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-module-corelist/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-module-corelist/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..f4457379cdfa31b5028a94047524d696ccdd6421 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-module-corelist/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-perl-module-corelist: + build: . + image: mcp-perl-module-corelist:latest + container_name: mcp-perl-module-corelist + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=perl-module-corelist + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-module-corelist/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-module-corelist/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..6ee1d6223feabda981b56e7d8a70e5387c0c6cc9 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-module-corelist/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - perl-module-corelist + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-module-corelist/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-module-corelist/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-module-corelist/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-module-runtime-conflicts/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-module-runtime-conflicts/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..36ae1de89a3a46a5207eb1e7126337ea9c0de0dd --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-module-runtime-conflicts/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install perl-module-runtime-conflicts via conda (e.g., from bioconda) +RUN conda install -c bioconda perl-module-runtime-conflicts -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY app/perl-module-runtime-conflicts_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/perl-module-runtime-conflicts_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/perl-module-runtime-conflicts_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-module-runtime-conflicts/app/perl-module-runtime-conflicts_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-module-runtime-conflicts/app/perl-module-runtime-conflicts_server.py new file mode 100644 index 0000000000000000000000000000000000000000..cb55048b62f96a1f4082a3d2b26acb99c4aeb011 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-module-runtime-conflicts/app/perl-module-runtime-conflicts_server.py @@ -0,0 +1,89 @@ +import subprocess +import tempfile +from pathlib import Path +from typing import Dict, List, Any + +# @mcp.tool() is a placeholder for the actual decorator. +# The user does not want the import statement for mcp. +class mcp: + def tool(func): + return func + +@mcp.tool +def check_module_runtime_conflicts() -> Dict[str, Any]: + """ + Checks for version conflicts related to the Perl module Module::Runtime. + + This tool executes a small Perl script that uses Module::Runtime::Conflicts + to check for known version conflicts with other installed Perl modules. + If conflicts are found, the tool will exit with an error and print details + to stderr. If no conflicts are found, it will report success to stdout. + This functionality is typically used by other tools like 'moose-outdated'. + """ + # The Perl code to execute, based on the module's SYNOPSIS. + # The check_conflicts() function 'dies' (exits with an error) if a conflict is found. + # We wrap it in an eval block to catch the error message and control the output. + perl_script_content = """ + use strict; + use warnings; + use Module::Runtime::Conflicts; + + eval { + Module::Runtime::Conflicts->check_conflicts; + }; + if ($@) { + # The check_conflicts function dies on conflict. + # We catch it, print the error message to STDERR, and exit with an error code. + print STDERR $@; + exit 1; + } + + print "No conflicts found for Module::Runtime.\\n"; + exit 0; + """ + + script_path = None + try: + # Using a temporary file is robust for multi-line scripts. + with tempfile.NamedTemporaryFile(mode='w', delete=False, suffix=".pl", encoding='utf-8') as temp_script: + script_path_str = temp_script.name + temp_script.write(perl_script_content) + + script_path = Path(script_path_str) + command = ["perl", str(script_path)] + command_str = " ".join(command) + + # Execute the perl script + result = subprocess.run( + command, + capture_output=True, + text=True, + check=True + ) + + return { + "command_executed": command_str, + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": [] + } + except FileNotFoundError: + # This happens if 'perl' is not in the system's PATH + return { + "command_executed": "perl ", + "stdout": "", + "stderr": "Error: 'perl' executable not found. Please ensure Perl is installed and in your system's PATH.", + "output_files": [] + } + except subprocess.CalledProcessError as e: + # This happens if the script returns a non-zero exit code (i.e., conflicts found) + return { + "command_executed": " ".join(["perl", str(script_path)]) if script_path else "perl ", + "stdout": e.stdout, + "stderr": e.stderr, + "output_files": [] + } + finally: + # Ensure the temporary file is always cleaned up + if script_path and script_path.exists(): + script_path.unlink() diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-module-runtime-conflicts/app/perl-module-runtime-conflicts_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-module-runtime-conflicts/app/perl-module-runtime-conflicts_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..980a51e3bd5b6ba3fed58d5d27a13bf49ac682da --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-module-runtime-conflicts/app/perl-module-runtime-conflicts_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-module-runtime-conflicts/app/perl-module-runtime-conflicts_server.py') +SERVER_NAME = 'biosci_perl_module_runtime_conflicts' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-module-runtime-conflicts/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-module-runtime-conflicts/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-module-runtime-conflicts/app/requirements.txt @@ -0,0 +1 @@ + diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-module-runtime-conflicts/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-module-runtime-conflicts/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..4821a20f946b707cfb7ad0b5900028ad713249f5 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-module-runtime-conflicts/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-perl-module-runtime-conflicts: + build: . + image: mcp-perl-module-runtime-conflicts:latest + container_name: mcp-perl-module-runtime-conflicts + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=perl-module-runtime-conflicts + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-module-runtime-conflicts/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-module-runtime-conflicts/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..aa0fe5e007443813c16fc0c0a824e7f062e4817d --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-module-runtime-conflicts/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - perl-module-runtime-conflicts + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-module-runtime-conflicts/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-module-runtime-conflicts/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-module-runtime-conflicts/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-module-runtime/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-module-runtime/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..d293c58ae5adaee852045a01914f241450964afd --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-module-runtime/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install perl-module-runtime via conda (e.g., from bioconda) +RUN conda install -c bioconda perl-module-runtime -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY app/perl-module-runtime_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/perl-module-runtime_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/perl-module-runtime_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-module-runtime/app/perl-module-runtime_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-module-runtime/app/perl-module-runtime_server.py new file mode 100644 index 0000000000000000000000000000000000000000..ba879bee7a8418bb4628f457287e3fc55595f1d1 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-module-runtime/app/perl-module-runtime_server.py @@ -0,0 +1,26 @@ +from typing import Optional, List +import subprocess +from pathlib import Path +import tempfile + +@mcp.tool() +def perl_module_runtime_info(): + """ + This function serves as a placeholder to address the request for 'perl-module-runtime'. + + Please note: 'perl-module-runtime' (Module::Runtime on CPAN) is a Perl *library* + designed for managing module loading at runtime within Perl scripts. It does not + provide any standalone command-line executables or subcommands that can be + directly wrapped as an MCP server tool. + + Therefore, there are no command-line parameters, subcommands, or internal functions + to parse from documentation or execute via subprocess. This function merely + informs about the nature of the specified Perl module. + """ + return { + "command_executed": "No command executed as 'perl-module-runtime' is a Perl library.", + "stdout": "Module::Runtime is a Perl library, not a command-line tool. It does not expose any command-line interface or subcommands to wrap.", + "stderr": "Cannot wrap 'perl-module-runtime' as an MCP tool because it lacks a command-line interface.", + "output_files": [], + "error": "The specified tool 'perl-module-runtime' is a Perl library, not a command-line executable. It does not have any command-line functions to convert into an MCP server tool." + } \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-module-runtime/app/perl-module-runtime_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-module-runtime/app/perl-module-runtime_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..0796bd248d19de7caf17778281f823b141eef481 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-module-runtime/app/perl-module-runtime_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-module-runtime/app/perl-module-runtime_server.py') +SERVER_NAME = 'biosci_perl_module_runtime' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-module-runtime/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-module-runtime/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-module-runtime/app/requirements.txt @@ -0,0 +1 @@ + diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-module-runtime/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-module-runtime/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..65e751a2099ab1b714993ff0135d8d012e8fca69 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-module-runtime/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-perl-module-runtime: + build: . + image: mcp-perl-module-runtime:latest + container_name: mcp-perl-module-runtime + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=perl-module-runtime + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-module-runtime/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-module-runtime/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..9285fdc54765621f56bebc5df4dc01170f124bb3 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-module-runtime/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - perl-module-runtime + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-module-runtime/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-module-runtime/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-module-runtime/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-net-http/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-net-http/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..6ad4e3530ddbd07d922adf2c273904168581494b --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-net-http/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - perl-net-http + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-params-util/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-params-util/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..38698618f3c5bdb5d9fa879f2b25083281817959 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-params-util/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install perl-params-util via conda (e.g., from bioconda) +RUN conda install -c bioconda perl-params-util -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY app/perl-params-util_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/perl-params-util_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/perl-params-util_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-params-util/app/perl-params-util_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-params-util/app/perl-params-util_server.py new file mode 100644 index 0000000000000000000000000000000000000000..2dc136ad7bdd31824a73445e7d1a04a3888210f1 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-params-util/app/perl-params-util_server.py @@ -0,0 +1,744 @@ +import subprocess +from pathlib import Path +from typing import Optional, List, Dict, Union, Any + +# Helper function to run Perl commands +def _run_perl_util_command( + perl_path: Path, + function_name: str, # The specific Params::Util function to import + perl_code: str, # The Perl code snippet to execute + additional_imports: Optional[List[str]] = None, # Other modules to import +) -> Dict[str, Any]: + """ + Executes a Perl one-liner using Params::Util and captures output. + """ + if not perl_path.is_file(): + raise FileNotFoundError(f"Perl executable not found at {perl_path}") + + # Construct the Perl module import string + # We always import the specific function_name, and optionally others + imports = [function_name] + if additional_imports: + imports.extend(additional_imports) + + # Ensure unique imports + imports = list(set(imports)) + + # The -M flag expects a comma-separated list of functions for Params::Util=qw(...) + # and a space-separated list for other modules. + # For simplicity, we'll assume Params::Util functions are passed as qw(...) + # and other modules are just 'use Module;' + + module_import_parts = [f"-MParams::Util=qw({' '.join(imports)})"] + if additional_imports: + # Add other modules as separate -M flags or within the -e block + # For simplicity, we'll add them to the -e block for now. + # If they were to be added via -M, it would be like -MScalar::Util + pass # Handled by including 'use Scalar::Util;' in perl_code if needed + + command = [ + str(perl_path), + *module_import_parts, + "-e", + perl_code, + ] + + try: + process = subprocess.run( + command, + capture_output=True, + text=True, + check=True, + ) + stdout = process.stdout.strip() + stderr = process.stderr.strip() + + # Params::Util functions return undef on failure, which we print as 'undef' + # Map 'undef' string output to Python None + result_value = None if stdout == "undef" else stdout + + return { + "command_executed": " ".join(command), + "stdout": stdout, + "stderr": stderr, + "result": result_value, + "output_files": [], + } + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(command), + "stdout": e.stdout.strip(), + "stderr": e.stderr.strip(), + "error": f"Perl command failed with exit code {e.returncode}", + "result": None, + "output_files": [], + } + except FileNotFoundError: + return { + "command_executed": " ".join(command), + "stdout": "", + "stderr": f"Error: Perl executable not found at {perl_path}", + "error": f"Perl executable not found at {perl_path}", + "result": None, + "output_files": [], + } + +@mcp.tool() +def _string( + value: str, + perl_path: Path = Path("perl"), +) -> Dict[str, Any]: + """ + Tests if a value is a normal non-false string of non-zero length using Params::Util::_STRING. + Returns the string if valid, or None otherwise. + """ + if not isinstance(value, str): + raise TypeError("Value must be a string.") + if not value: + # _STRING specifically checks for non-zero length + return { + "command_executed": "", + "stdout": "", + "stderr": "Input value is an empty string, which _STRING will treat as invalid.", + "result": None, + "output_files": [], + } + + perl_code = f"my $result = _STRING('{value}'); print defined $result ? $result : 'undef';" + return _run_perl_util_command(perl_path, "_STRING", perl_code) + +@mcp.tool() +def _identifier( + value: str, + perl_path: Path = Path("perl"), +) -> Dict[str, Any]: + """ + Tests if a value is a string that is a valid Perl identifier using Params::Util::_IDENTIFIER. + Returns the string if valid, or None otherwise. + """ + if not isinstance(value, str): + raise TypeError("Value must be a string.") + + perl_code = f"my $result = _IDENTIFIER('{value}'); print defined $result ? $result : 'undef';" + return _run_perl_util_command(perl_path, "_IDENTIFIER", perl_code) + +@mcp.tool() +def _class( + value: str, + perl_path: Path = Path("perl"), +) -> Dict[str, Any]: + """ + Tests if a value is a string that is a valid Perl class name using Params::Util::_CLASS. + Checks format only, not if the class is loaded. Assumes "normalized" form. + Returns the string if valid, or None otherwise. + """ + if not isinstance(value, str): + raise TypeError("Value must be a string.") + + perl_code = f"my $result = _CLASS('{value}'); print defined $result ? $result : 'undef';" + return _run_perl_util_command(perl_path, "_CLASS", perl_code) + +@mcp.tool() +def _classisa( + value: str, + class_name: str, + perl_path: Path = Path("perl"), +) -> Dict[str, Any]: + """ + Tests if a value is a string that is a particular class, or a subclass of it, + using Params::Util::_CLASSISA. Calls the ->isa method on the class name. + Returns the string if valid, or None otherwise. + """ + if not isinstance(value, str) or not isinstance(class_name, str): + raise TypeError("Value and class_name must be strings.") + + perl_code = f"my $result = _CLASSISA('{value}', '{class_name}'); print defined $result ? $result : 'undef';" + return _run_perl_util_command(perl_path, "_CLASSISA", perl_code) + +@mcp.tool() +def _classdoes( + value: str, + role_name: str, + perl_path: Path = Path("perl"), +) -> Dict[str, Any]: + """ + Tests if a value is a string that is a particular class, or a class that DOES a role, + using Params::Util::_CLASSDOES. Calls the ->DOES method on the class name. + Recommended for Perl 5.10 or later. + Returns the string if valid, or None otherwise. + """ + if not isinstance(value, str) or not isinstance(role_name, str): + raise TypeError("Value and role_name must be strings.") + + perl_code = f"my $result = _CLASSDOES('{value}', '{role_name}'); print defined $result ? $result : 'undef';" + return _run_perl_util_command(perl_path, "_CLASSDOES", perl_code) + +@mcp.tool() +def _subclass( + value: str, + class_name: str, + perl_path: Path = Path("perl"), +) -> Dict[str, Any]: + """ + Tests if a value is a string that is a subclass of a specified class, + using Params::Util::_SUBCLASS. Calls the ->isa method on the class name. + Returns the string if valid, or None otherwise. + """ + if not isinstance(value, str) or not isinstance(class_name, str): + raise TypeError("Value and class_name must be strings.") + + perl_code = f"my $result = _SUBCLASS('{value}', '{class_name}'); print defined $result ? $result : 'undef';" + return _run_perl_util_command(perl_path, "_SUBCLASS", perl_code) + +@mcp.tool() +def _number( + value: Union[str, int, float], + perl_path: Path = Path("perl"), +) -> Dict[str, Any]: + """ + Tests if a value is a number (defined and Perl thinks it's a number) + using Params::Util::_NUMBER. + Returns the value if valid, or None otherwise. + """ + # Perl's looks_like_number is quite flexible, so we pass the string representation + perl_value = str(value) + perl_code = f"use Scalar::Util qw(looks_like_number); my $result = _NUMBER('{perl_value}'); print defined $result ? $result : 'undef';" + return _run_perl_util_command(perl_path, "_NUMBER", perl_code, additional_imports=["Scalar::Util"]) + +@mcp.tool() +def _posint( + value: Union[str, int], + perl_path: Path = Path("perl"), +) -> Dict[str, Any]: + """ + Tests if a value is a positive integer using Params::Util::_POSINT. + Returns the value if valid, or None otherwise. + """ + perl_value = str(value) + perl_code = f"my $result = _POSINT('{perl_value}'); print defined $result ? $result : 'undef';" + return _run_perl_util_command(perl_path, "_POSINT", perl_code) + +@mcp.tool() +def _nonnegint( + value: Union[str, int], + perl_path: Path = Path("perl"), +) -> Dict[str, Any]: + """ + Tests if a value is a non-negative integer (positive integer or zero) + using Params::Util::_NONNEGINT. + Returns the value if valid, or None otherwise. + """ + perl_value = str(value) + perl_code = f"my $result = _NONNEGINT('{perl_value}'); print defined $result ? $result : 'undef';" + return _run_perl_util_command(perl_path, "_NONNEGINT", perl_code) + +@mcp.tool() +def _scalar( + value_content: str, + perl_path: Path = Path("perl"), +) -> Dict[str, Any]: + """ + Tests for a raw and unblessed SCALAR reference with non-zero length content + using Params::Util::_SCALAR. + The Python 'value_content' is used to create a scalar reference in Perl. + Returns the dereferenced scalar content if valid, or None otherwise. + """ + # Perl code creates a scalar variable, takes a reference to it, and passes the reference. + # Then it prints the dereferenced value if valid. + perl_code = f""" + my $val = '{value_content}'; + my $ref = \\$val; + my $result = _SCALAR($ref); + print defined $result ? $$result : 'undef'; + """ + return _run_perl_util_command(perl_path, "_SCALAR", perl_code) + +@mcp.tool() +def _scalar0( + value_content: str, + perl_path: Path = Path("perl"), +) -> Dict[str, Any]: + """ + Tests for a raw and unblessed SCALAR reference, allowing zero-length content, + using Params::Util::_SCALAR0. + The Python 'value_content' is used to create a scalar reference in Perl. + Returns the dereferenced scalar content if valid, or None otherwise. + """ + perl_code = f""" + my $val = '{value_content}'; + my $ref = \\$val; + my $result = _SCALAR0($ref); + print defined $result ? $$result : 'undef'; + """ + return _run_perl_util_command(perl_path, "_SCALAR0", perl_code) + +@mcp.tool() +def _array( + values: List[str], + perl_path: Path = Path("perl"), +) -> Dict[str, Any]: + """ + Tests for a raw and unblessed ARRAY reference containing at least one element + using Params::Util::_ARRAY. + The Python 'values' list is used to create an array reference in Perl. + Returns a comma-separated string of dereferenced array elements if valid, or None otherwise. + """ + if not isinstance(values, list): + raise TypeError("Values must be a list of strings.") + if not values: + # _ARRAY specifically checks for at least one element + return { + "command_executed": "", + "stdout": "", + "stderr": "Input values list is empty, which _ARRAY will treat as invalid.", + "result": None, + "output_files": [], + } + + perl_array_elements = ', '.join(f"'{v}'" for v in values) + perl_code = f""" + my @arr = ({perl_array_elements}); + my $ref = \\@arr; + my $result = _ARRAY($ref); + print defined $result ? join(',', @$result) : 'undef'; + """ + return _run_perl_util_command(perl_path, "_ARRAY", perl_code) + +@mcp.tool() +def _array0( + values: List[str], + perl_path: Path = Path("perl"), +) -> Dict[str, Any]: + """ + Tests for a raw and unblessed ARRAY reference, allowing zero-length content, + using Params::Util::_ARRAY0. + The Python 'values' list is used to create an array reference in Perl. + Returns a comma-separated string of dereferenced array elements if valid, or None otherwise. + """ + if not isinstance(values, list): + raise TypeError("Values must be a list of strings.") + + perl_array_elements = ', '.join(f"'{v}'" for v in values) + perl_code = f""" + my @arr = ({perl_array_elements}); + my $ref = \\@arr; + my $result = _ARRAY0($ref); + print defined $result ? join(',', @$result) : 'undef'; + """ + return _run_perl_util_command(perl_path, "_ARRAY0", perl_code) + +@mcp.tool() +def _arraylike( + value: str, + perl_path: Path = Path("perl"), +) -> Dict[str, Any]: + """ + Tests whether a given scalar value can respond to array dereferencing + using Params::Util::_ARRAYLIKE. + Note: This function is difficult to test generically via `perl -e` with simple strings, + as it typically expects an actual Perl reference or an object that overloads array dereferencing. + Passing a simple string will likely result in 'undef'. + Returns the value if it's array-like, or None otherwise. + """ + if not isinstance(value, str): + raise TypeError("Value must be a string.") + + perl_code = f""" + my $val = '{value}'; + my $result = _ARRAYLIKE($val); + print defined $result ? $result : 'undef'; + """ + return _run_perl_util_command(perl_path, "_ARRAYLIKE", perl_code) + +@mcp.tool() +def _hash( + values: Dict[str, str], + perl_path: Path = Path("perl"), +) -> Dict[str, Any]: + """ + Tests for a raw and unblessed HASH reference with at least one entry + using Params::Util::_HASH. + The Python 'values' dictionary is used to create a hash reference in Perl. + Returns a comma-separated string of key-value pairs (key=>value) if valid, or None otherwise. + """ + if not isinstance(values, dict): + raise TypeError("Values must be a dictionary of strings.") + if not values: + # _HASH specifically checks for at least one entry + return { + "command_executed": "", + "stdout": "", + "stderr": "Input values dictionary is empty, which _HASH will treat as invalid.", + "result": None, + "output_files": [], + } + + perl_hash_elements = ', '.join(f"'{k}' => '{v}'" for k, v in values.items()) + perl_code = f""" + my %hash = ({perl_hash_elements}); + my $ref = \\%hash; + my $result = _HASH($ref); + print defined $result ? join(',', map {{ "$_=>$hash{{$_}}" }} sort keys %$result) : 'undef'; + """ + return _run_perl_util_command(perl_path, "_HASH", perl_code) + +@mcp.tool() +def _hash0( + values: Dict[str, str], + perl_path: Path = Path("perl"), +) -> Dict[str, Any]: + """ + Tests for a raw and unblessed HASH reference, regardless of content, + using Params::Util::_HASH0. + The Python 'values' dictionary is used to create a hash reference in Perl. + Returns a comma-separated string of key-value pairs (key=>value) if valid, or None otherwise. + """ + if not isinstance(values, dict): + raise TypeError("Values must be a dictionary of strings.") + + perl_hash_elements = ', '.join(f"'{k}' => '{v}'" for k, v in values.items()) + perl_code = f""" + my %hash = ({perl_hash_elements}); + my $ref = \\%hash; + my $result = _HASH0($ref); + print defined $result ? join(',', map {{ "$_=>$hash{{$_}}" }} sort keys %$result) : 'undef'; + """ + return _run_perl_util_command(perl_path, "_HASH0", perl_code) + +@mcp.tool() +def _hashlike( + value: str, + perl_path: Path = Path("perl"), +) -> Dict[str, Any]: + """ + Tests whether a given scalar value can respond to hash dereferencing + using Params::Util::_HASHLIKE. + Note: This function is difficult to test generically via `perl -e` with simple strings, + as it typically expects an actual Perl reference or an object that overloads hash dereferencing. + Passing a simple string will likely result in 'undef'. + Returns the value if it's hash-like, or None otherwise. + """ + if not isinstance(value, str): + raise TypeError("Value must be a string.") + + perl_code = f""" + my $val = '{value}'; + my $result = _HASHLIKE($val); + print defined $result ? $result : 'undef'; + """ + return _run_perl_util_command(perl_path, "_HASHLIKE", perl_code) + +@mcp.tool() +def _code( + code_string: str, + perl_path: Path = Path("perl"), +) -> Dict[str, Any]: + """ + Tests for a raw and unblessed CODE reference using Params::Util::_CODE. + The 'code_string' is evaluated in Perl to create a code reference. + WARNING: Evaluating arbitrary code from user input can be a security risk. + Returns 'CODE_REF' if a valid code reference is created and validated, or None otherwise. + """ + if not isinstance(code_string, str): + raise TypeError("Code string must be a string.") + + perl_code = f""" + my $code_ref; + eval '$code_ref = sub {{ {code_string} }};'; + if ($@) {{ + print STDERR "Perl eval error: $@"; + print "undef"; + }} else {{ + my $result = _CODE($code_ref); + print defined $result ? 'CODE_REF' : 'undef'; + }} + """ + return _run_perl_util_command(perl_path, "_CODE", perl_code) + +@mcp.tool() +def _codelike( + code_string: str, + perl_path: Path = Path("perl"), +) -> Dict[str, Any]: + """ + Tests whether a given scalar value can respond to code dereferencing (like _CODE, + but also includes objects overloading '&{}') using Params::Util::_CODELIKE. + The 'code_string' is evaluated in Perl to create a code reference. + WARNING: Evaluating arbitrary code from user input can be a security risk. + Returns 'CODE_REF' if a valid callable is created and validated, or None otherwise. + """ + if not isinstance(code_string, str): + raise TypeError("Code string must be a string.") + + perl_code = f""" + my $code_ref; + eval '$code_ref = sub {{ {code_string} }};'; + if ($@) {{ + print STDERR "Perl eval error: $@"; + print "undef"; + }} else {{ + my $result = _CODELIKE($code_ref); + print defined $result ? 'CODE_REF' : 'undef'; + }} + """ + return _run_perl_util_command(perl_path, "_CODELIKE", perl_code) + +@mcp.tool() +def _invocant( + value: str, + perl_path: Path = Path("perl"), +) -> Dict[str, Any]: + """ + Tests whether the given value is a valid method invocant (object instance or class name) + using Params::Util::_INVOCANT. + Returns the value if it's a valid invocant, or None otherwise. + """ + if not isinstance(value, str): + raise TypeError("Value must be a string.") + + perl_code = f"my $result = _INVOCANT('{value}'); print defined $result ? $result : 'undef';" + return _run_perl_util_command(perl_path, "_INVOCANT", perl_code) + +@mcp.tool() +def _instance( + object_class_name: str, + expected_class: str, + perl_path: Path = Path("perl"), +) -> Dict[str, Any]: + """ + Tests for an object of a particular class in a strictly correct manner + using Params::Util::_INSTANCE. + The 'object_class_name' is used to attempt to instantiate an object in Perl. + Returns the class name of the object if valid, or None otherwise. + Note: This requires the 'object_class_name' to be a loadable Perl module with a 'new' constructor that takes no arguments. + """ + if not isinstance(object_class_name, str) or not isinstance(expected_class, str): + raise TypeError("Object class name and expected class must be strings.") + + # Attempt to instantiate the object in Perl + perl_code = f""" + my $obj; + eval "use {object_class_name};"; + if ($@) {{ + print STDERR "Perl module load error for {object_class_name}: $@"; + print "undef"; + }} else {{ + eval '$obj = {object_class_name}->new();'; + if ($@) {{ + print STDERR "Perl object instantiation error for {object_class_name}: $@"; + print "undef"; + }} else {{ + my $result = _INSTANCE($obj, '{expected_class}'); + print defined $result ? ref($result) : 'undef'; + }} + }} + """ + return _run_perl_util_command(perl_path, "_INSTANCE", perl_code) + +@mcp.tool() +def _instancedoes( + object_class_name: str, + role_name: str, + perl_path: Path = Path("perl"), +) -> Dict[str, Any]: + """ + Tests for an object that DOES a particular role, similar to _INSTANCE, + using Params::Util::_INSTANCEDOES. + The 'object_class_name' is used to attempt to instantiate an object in Perl. + Returns the class name of the object if valid, or None otherwise. + Note: This requires the 'object_class_name' to be a loadable Perl module with a 'new' constructor that takes no arguments. + Recommended for Perl 5.10 or later. + """ + if not isinstance(object_class_name, str) or not isinstance(role_name, str): + raise TypeError("Object class name and role name must be strings.") + + perl_code = f""" + my $obj; + eval "use {object_class_name};"; + if ($@) {{ + print STDERR "Perl module load error for {object_class_name}: $@"; + print "undef"; + }} else {{ + eval '$obj = {object_class_name}->new();'; + if ($@) {{ + print STDERR "Perl object instantiation error for {object_class_name}: $@"; + print "undef"; + }} else {{ + my $result = _INSTANCEDOES($obj, '{role_name}'); + print defined $result ? ref($result) : 'undef'; + }} + }} + """ + return _run_perl_util_command(perl_path, "_INSTANCEDOES", perl_code) + +@mcp.tool() +def _regex( + pattern: str, + perl_path: Path = Path("perl"), +) -> Dict[str, Any]: + """ + Tests for a regular expression using Params::Util::_REGEX. + The 'pattern' string is converted to a Perl regex object (qr{}) for testing. + Returns the pattern string if it's a valid regex, or None otherwise. + """ + if not isinstance(pattern, str): + raise TypeError("Pattern must be a string.") + + # Perl code creates a regex object from the pattern string + perl_code = f""" + my $regex_obj; + eval '$regex_obj = qr{{{pattern}}};'; + if ($@) {{ + print STDERR "Perl regex compilation error: $@"; + print "undef"; + }} else {{ + my $result = _REGEX($regex_obj); + print defined $result ? '{pattern}' : 'undef'; # Return the original pattern if valid + }} + """ + return _run_perl_util_command(perl_path, "_REGEX", perl_code) + +@mcp.tool() +def _set( + num_objects: int, + class_name: str, + perl_path: Path = Path("perl"), +) -> Dict[str, Any]: + """ + Tests for a set of at least one object of a particular class using Params::Util::_SET. + 'num_objects' specifies how many objects of 'class_name' to instantiate in Perl. + Returns a comma-separated string of dereferenced object class names if valid, or None otherwise. + Note: This requires 'class_name' to be a loadable Perl module with a 'new' constructor that takes no arguments. + """ + if not isinstance(num_objects, int) or num_objects < 0: + raise ValueError("num_objects must be a non-negative integer.") + if not isinstance(class_name, str): + raise TypeError("class_name must be a string.") + if num_objects == 0: + # _SET specifically checks for at least one object + return { + "command_executed": "", + "stdout": "", + "stderr": "num_objects is 0, which _SET will treat as invalid (requires at least one object).", + "result": None, + "output_files": [], + } + + # Construct Perl code to instantiate objects and create an array reference + perl_object_instantiation = [] + for i in range(num_objects): + perl_object_instantiation.append(f"eval '$obj_{i} = {class_name}->new();' or die $@;") + perl_object_instantiation.append(f"push @arr, $obj_{i};") + + perl_code = f""" + my @arr; + eval "use {class_name};"; + if ($@) {{ + print STDERR "Perl module load error for {class_name}: $@"; + print "undef"; + }} else {{ + {' '.join(perl_object_instantiation)} + my $ref = \\@arr; + my $result = _SET($ref, '{class_name}'); + print defined $result ? join(',', map {{ ref($_) }} @$result) : 'undef'; + }} + """ + return _run_perl_util_command(perl_path, "_SET", perl_code) + +@mcp.tool() +def _set0( + num_objects: int, + class_name: str, + perl_path: Path = Path("perl"), +) -> Dict[str, Any]: + """ + Tests for a set of objects of a particular class, allowing zero objects, + using Params::Util::_SET0. + 'num_objects' specifies how many objects of 'class_name' to instantiate in Perl. + Returns a comma-separated string of dereferenced object class names if valid, or None otherwise. + Note: This requires 'class_name' to be a loadable Perl module with a 'new' constructor that takes no arguments. + """ + if not isinstance(num_objects, int) or num_objects < 0: + raise ValueError("num_objects must be a non-negative integer.") + if not isinstance(class_name, str): + raise TypeError("class_name must be a string.") + + # Construct Perl code to instantiate objects and create an array reference + perl_object_instantiation = [] + for i in range(num_objects): + perl_object_instantiation.append(f"eval '$obj_{i} = {class_name}->new();' or die $@;") + perl_object_instantiation.append(f"push @arr, $obj_{i};") + + perl_code = f""" + my @arr; + eval "use {class_name};"; + if ($@) {{ + print STDERR "Perl module load error for {class_name}: $@"; + print "undef"; + }} else {{ + {' '.join(perl_object_instantiation)} + my $ref = \\@arr; + my $result = _SET0($ref, '{class_name}'); + print defined $result ? join(',', map {{ ref($_) }} @$result) : 'undef'; + }} + """ + return _run_perl_util_command(perl_path, "_SET0", perl_code) + +@mcp.tool() +def _handle( + file_path: Optional[Path] = None, + perl_path: Path = Path("perl"), +) -> Dict[str, Any]: + """ + Tests whether a single scalar value is a file handle using Params::Util::_HANDLE. + If 'file_path' is provided, Perl attempts to open it and checks the resulting handle. + If 'file_path' is None, it checks STDIN. + Returns 'HANDLE_OK' if a valid file handle is detected, or None otherwise. + """ + if file_path is not None and not isinstance(file_path, Path): + raise TypeError("file_path must be a Path object or None.") + + if file_path: + # Ensure the file exists if provided + if not file_path.is_file(): + return { + "command_executed": "", + "stdout": "", + "stderr": f"Input file not found at {file_path}", + "result": None, + "output_files": [], + } + perl_code = f""" + open my $fh, '<', '{file_path}' or die "Could not open file {file_path}: $!"; + my $result = _HANDLE($fh); + close $fh; + print defined $result ? 'HANDLE_OK' : 'undef'; + """ + else: + # Check STDIN if no file path is provided + perl_code = """ + my $result = _HANDLE(*STDIN{IO}); + print defined $result ? 'HANDLE_OK' : 'undef'; + """ + return _run_perl_util_command(perl_path, "_HANDLE", perl_code) + +@mcp.tool() +def _driver( + class_name: str, + base_driver_class: str, + perl_path: Path = Path("perl"), +) -> Dict[str, Any]: + """ + Loads and validates a driver class using Params::Util::_DRIVER. + Checks that 'class_name' is a valid class, loads it, and ensures it returns true + for the 'isa' method on 'base_driver_class'. + Returns the class name if valid, or None otherwise. + """ + if not isinstance(class_name, str) or not isinstance(base_driver_class, str): + raise TypeError("Class name and base driver class must be strings.") + + perl_code = f""" + my $result = _DRIVER('{class_name}', '{base_driver_class}'); + print defined $result ? $result : 'undef'; + """ + return _run_perl_util_command(perl_path, "_DRIVER", perl_code) \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-params-util/app/perl-params-util_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-params-util/app/perl-params-util_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..1d26f5e82499f168c45bebb653244e53d2b9f69a --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-params-util/app/perl-params-util_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-params-util/app/perl-params-util_server.py') +SERVER_NAME = 'biosci_perl_params_util' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-params-util/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-params-util/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-params-util/app/requirements.txt @@ -0,0 +1 @@ + diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-params-util/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-params-util/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..9bbed5b605328a259b48223495990a2c85112ac8 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-params-util/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-perl-params-util: + build: . + image: mcp-perl-params-util:latest + container_name: mcp-perl-params-util + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=perl-params-util + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-params-util/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-params-util/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..e3c65947edd91d4c4f1bb24a35720f1218527dbf --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-params-util/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - perl-params-util + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-params-util/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-params-util/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-params-util/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-parent/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-parent/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..43064fcee75307587503cd4428a6bb8e4a229a78 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-parent/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install perl-parent via conda (e.g., from bioconda) +RUN conda install -c bioconda perl-parent -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY app/perl-parent_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/perl-parent_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/perl-parent_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-parent/app/perl-parent_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-parent/app/perl-parent_server.py new file mode 100644 index 0000000000000000000000000000000000000000..8475f87cb91bc05bf3a568c88f38e8d532c0d8fe --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-parent/app/perl-parent_server.py @@ -0,0 +1,346 @@ +import subprocess +from pathlib import Path +from typing import Optional, List, Dict, Any + +@mcp.tool() +def samtools_view( + input_file: str, + output_file: Optional[str] = None, + is_bam: bool = False, + is_cram: bool = False, + uncompressed: bool = False, + fast_compression: bool = False, + count: bool = False, + header_only: bool = False, + include_header: bool = False, + threads: int = 1, + subsample_fraction: float = 0.0, + region: Optional[str] = None, + min_mq: int = 0, + require_flags: int = 0, + exclude_flags: int = 0, +): + """ + Views and converts SAM/BAM/CRAM files. + + Args: + input_file: Path to the input SAM, BAM, or CRAM file. + output_file: Path to the output file. + is_bam: Output BAM format. + is_cram: Output CRAM format. + uncompressed: Output uncompressed BAM. + fast_compression: Use fast BAM compression. + count: Instead of printing the alignments, only count them. + header_only: Output the header only. + include_header: Include the header in the output. + threads: Number of additional threads to use. + subsample_fraction: Fraction of templates/reads to subsample [0.0 to 1.0]. + region: Optional genomic region (e.g., 'chr1:100-200'). + min_mq: Skip alignments with MAPQ smaller than this value. + require_flags: Only include reads with all bits set in INT present in the FLAG field. + exclude_flags: Only include reads with none of the bits set in INT present in the FLAG field. + """ + input_path = Path(input_file) + if not input_path.exists(): + return {"error": f"Input file {input_file} does not exist."} + + cmd = ["samtools", "view"] + + if is_bam: cmd.append("-b") + if is_cram: cmd.append("-C") + if uncompressed: cmd.append("-u") + if fast_compression: cmd.append("-1") + if count: cmd.append("-c") + if header_only: cmd.append("-H") + if include_header: cmd.append("-h") + if threads > 1: cmd.extend(["-@", str(threads)]) + if subsample_fraction > 0.0: cmd.extend(["-s", str(subsample_fraction)]) + if min_mq > 0: cmd.extend(["-q", str(min_mq)]) + if require_flags > 0: cmd.extend(["-f", str(require_flags)]) + if exclude_flags > 0: cmd.extend(["-F", str(exclude_flags)]) + + if output_file: + cmd.extend(["-o", output_file]) + + cmd.append(input_file) + + if region: + cmd.append(region) + + try: + result = subprocess.run(cmd, capture_output=True, text=True, check=True) + return { + "command_executed": " ".join(cmd), + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": [output_file] if output_file else [] + } + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(cmd), + "error": str(e), + "stdout": e.stdout, + "stderr": e.stderr + } + +@mcp.tool() +def samtools_sort( + input_file: str, + output_file: str, + threads: int = 1, + memory_per_thread: str = "768M", + by_name: bool = False, + compression_level: int = -1, +): + """ + Sorts alignments by leftmost coordinates or by read name. + + Args: + input_file: Path to the input BAM/SAM/CRAM file. + output_file: Path to the output sorted file. + threads: Number of additional threads to use. + memory_per_thread: Maximum memory per thread; suffix K/M/G recognized. + by_name: Sort by read name instead of chromosomal coordinates. + compression_level: Set compression level, from 0 (uncompressed) to 9 (best). + """ + input_path = Path(input_file) + if not input_path.exists(): + return {"error": f"Input file {input_file} does not exist."} + + cmd = ["samtools", "sort", "-o", output_file] + + if threads > 1: cmd.extend(["-@", str(threads)]) + if memory_per_thread != "768M": cmd.extend(["-m", memory_per_thread]) + if by_name: cmd.append("-n") + if compression_level >= 0: cmd.extend(["-l", str(compression_level)]) + + cmd.append(input_file) + + try: + result = subprocess.run(cmd, capture_output=True, text=True, check=True) + return { + "command_executed": " ".join(cmd), + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": [output_file] + } + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(cmd), + "error": str(e), + "stdout": e.stdout, + "stderr": e.stderr + } + +@mcp.tool() +def samtools_index( + input_file: str, + output_index: Optional[str] = None, + csi: bool = False, + min_shift: int = 0, + threads: int = 1, +): + """ + Indexes a coordinate-sorted BAM or CRAM file. + + Args: + input_file: Path to the sorted BAM/CRAM file. + output_index: Optional path for the output index file. + csi: Generate CSI-format index instead of BAI. + min_shift: Set minimum interval size for CSI indices to 2^INT. + threads: Number of additional threads to use. + """ + input_path = Path(input_file) + if not input_path.exists(): + return {"error": f"Input file {input_file} does not exist."} + + cmd = ["samtools", "index"] + + if csi: cmd.append("-c") + if min_shift > 0: cmd.extend(["-m", str(min_shift)]) + if threads > 1: cmd.extend(["-@", str(threads)]) + + cmd.append(input_file) + if output_index: + cmd.append(output_index) + + try: + result = subprocess.run(cmd, capture_output=True, text=True, check=True) + return { + "command_executed": " ".join(cmd), + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": [output_index if output_index else f"{input_file}.bai"] + } + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(cmd), + "error": str(e), + "stdout": e.stdout, + "stderr": e.stderr + } + +@mcp.tool() +def samtools_flagstat( + input_file: str, + threads: int = 1, +): + """ + Provides simple statistics from the BAM FLAG field. + + Args: + input_file: Path to the BAM/SAM/CRAM file. + threads: Number of additional threads to use. + """ + input_path = Path(input_file) + if not input_path.exists(): + return {"error": f"Input file {input_file} does not exist."} + + cmd = ["samtools", "flagstat"] + if threads > 1: cmd.extend(["-@", str(threads)]) + cmd.append(input_file) + + try: + result = subprocess.run(cmd, capture_output=True, text=True, check=True) + return { + "command_executed": " ".join(cmd), + "stdout": result.stdout, + "stderr": result.stderr + } + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(cmd), + "error": str(e), + "stdout": e.stdout, + "stderr": e.stderr + } + +@mcp.tool() +def samtools_stats( + input_file: str, + reference: Optional[str] = None, + threads: int = 1, + remove_dups: bool = False, +): + """ + Generates comprehensive statistics from a BAM file. + + Args: + input_file: Path to the BAM/SAM/CRAM file. + reference: Optional path to the reference fasta file. + threads: Number of additional threads to use. + remove_dups: Exclude duplicate reads from statistics. + """ + input_path = Path(input_file) + if not input_path.exists(): + return {"error": f"Input file {input_file} does not exist."} + + cmd = ["samtools", "stats"] + if threads > 1: cmd.extend(["-@", str(threads)]) + if reference: cmd.extend(["--reference", reference]) + if remove_dups: cmd.append("-d") + cmd.append(input_file) + + try: + result = subprocess.run(cmd, capture_output=True, text=True, check=True) + return { + "command_executed": " ".join(cmd), + "stdout": result.stdout, + "stderr": result.stderr + } + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(cmd), + "error": str(e), + "stdout": e.stdout, + "stderr": e.stderr + } + +@mcp.tool() +def samtools_faidx( + fasta_file: str, + region: Optional[str] = None, + output_file: Optional[str] = None, +): + """ + Indexes or extracts regions from a FASTA file. + + Args: + fasta_file: Path to the reference FASTA file. + region: Optional genomic region to extract (e.g., 'chr1:100-200'). + output_file: Optional path to save the extracted sequence. + """ + fasta_path = Path(fasta_file) + if not fasta_path.exists(): + return {"error": f"Fasta file {fasta_file} does not exist."} + + cmd = ["samtools", "faidx", fasta_file] + if region: + cmd.append(region) + + if output_file: + cmd.extend(["-o", output_file]) + + try: + result = subprocess.run(cmd, capture_output=True, text=True, check=True) + return { + "command_executed": " ".join(cmd), + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": [output_file] if output_file else [f"{fasta_file}.fai"] + } + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(cmd), + "error": str(e), + "stdout": e.stdout, + "stderr": e.stderr + } + +@mcp.tool() +def samtools_depth( + input_file: str, + output_file: Optional[str] = None, + region: Optional[str] = None, + min_base_quality: int = 0, + min_mapping_quality: int = 0, +): + """ + Computes the depth of coverage at each genomic position. + + Args: + input_file: Path to the BAM/SAM/CRAM file. + output_file: Optional path to save the depth output. + region: Optional genomic region (e.g., 'chr1:100-200'). + min_base_quality: Minimum base quality. + min_mapping_quality: Minimum mapping quality. + """ + input_path = Path(input_file) + if not input_path.exists(): + return {"error": f"Input file {input_file} does not exist."} + + cmd = ["samtools", "depth"] + if min_base_quality > 0: cmd.extend(["-q", str(min_base_quality)]) + if min_mapping_quality > 0: cmd.extend(["-Q", str(min_mapping_quality)]) + if region: cmd.extend(["-r", region]) + + if output_file: + cmd.extend(["-o", output_file]) + + cmd.append(input_file) + + try: + result = subprocess.run(cmd, capture_output=True, text=True, check=True) + return { + "command_executed": " ".join(cmd), + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": [output_file] if output_file else [] + } + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(cmd), + "error": str(e), + "stdout": e.stdout, + "stderr": e.stderr + } \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-parent/app/perl-parent_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-parent/app/perl-parent_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..104c3f13e0c802eb9fc7e57c47b2138f365a3afc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-parent/app/perl-parent_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-parent/app/perl-parent_server.py') +SERVER_NAME = 'biosci_perl_parent' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-parent/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-parent/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-parent/app/requirements.txt @@ -0,0 +1 @@ + diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-parent/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-parent/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..efccfc3a2ad8f327f7d35bfee62fec77946b2c92 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-parent/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-perl-parent: + build: . + image: mcp-perl-parent:latest + container_name: mcp-perl-parent + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=perl-parent + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-parent/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-parent/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..381fd14ffc602071d09ab2fc5694246390b75e0b --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-parent/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - perl-parent + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-parent/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-parent/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-parent/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-sereal/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-sereal/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..c7fc5b8ec86f042e98e1a6bd6a6f3cdba4d8e7dc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-sereal/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install perl-sereal via conda (e.g., from bioconda) +RUN conda install -c bioconda perl-sereal -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY app/perl-sereal_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/perl-sereal_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/perl-sereal_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-sereal/app/perl-sereal_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-sereal/app/perl-sereal_server.py new file mode 100644 index 0000000000000000000000000000000000000000..60da427900f1c8d9c9fdb46c9ea362831b460d8f --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-sereal/app/perl-sereal_server.py @@ -0,0 +1,209 @@ +import subprocess +import shlex +from pathlib import Path +from typing import Optional, List + +# MCP decorator is omitted as per instructions, but would be present in a real MCP environment. +# @mcp.tool( +# name="perl-sereal", +# description="A wrapper for the Perl interpreter, the execution environment for the Sereal module." +# ) +def perl_interpreter( + programfile: Optional[Path] = None, + arguments: Optional[List[str]] = None, + execute_program: Optional[List[str]] = None, + execute_program_features: Optional[List[str]] = None, + check_syntax_only: bool = False, + print_version: bool = False, + print_config_variable: Optional[str] = None, + include_directories: Optional[List[Path]] = None, + use_module: Optional[List[str]] = None, + no_module: Optional[List[str]] = None, + loop_around_program: bool = False, + loop_and_print: bool = False, + autosplit: bool = False, + split_pattern: Optional[str] = None, + in_place_edit_extension: Optional[str] = None, + record_separator: Optional[str] = None, + line_terminator: Optional[str] = None, + enable_warnings: bool = False, + enable_all_warnings: bool = False, + disable_all_warnings: bool = False, + enable_tainting_checks: bool = False, + enable_tainting_warnings: bool = False, + allow_unsafe_operations: bool = False, + run_under_debugger: Optional[str] = None, + debugging_flags: Optional[str] = None, + unicode_features: Optional[str] = None, + no_sitecustomize: bool = False, + enable_switch_parsing: bool = False, + search_path_for_program: bool = False, + dump_core: bool = False, + ignore_text_before_shebang_dir: Optional[Path] = None, +) -> dict: + """ + Executes a Perl script or command. This tool wraps the `perl` interpreter, + which is the execution environment for Perl modules like Sereal. + + Args: + programfile: Path to the Perl program file to execute. + arguments: A list of arguments to pass to the program file. + execute_program: One or more lines of a program to be executed via the -e switch. + execute_program_features: Like execute_program, but enables all optional features (-E). + check_syntax_only: Check syntax only (runs BEGIN and CHECK blocks). Corresponds to -c. + print_version: Print version, patchlevel, and license. Corresponds to -v. + print_config_variable: Print configuration summary. If a value is provided, prints a single Config.pm variable. Corresponds to -V[:variable]. + include_directories: Specify @INC/#include directories. Corresponds to -I. + use_module: Execute "use module..." before the program. Corresponds to -M. + no_module: Execute "no module..." before the program. Corresponds to -m. + loop_around_program: Assume "while (<>) { ... }" loop around the program. Corresponds to -n. + loop_and_print: Assume loop like -n but also print the line. Corresponds to -p. + autosplit: Enable autosplit mode with -n or -p (splits $_ into @F). Corresponds to -a. + split_pattern: Specify the split() pattern for the -a switch. Corresponds to -F. + in_place_edit_extension: Edit <> files in place. If an extension is supplied, a backup is made. Corresponds to -i[extension]. + record_separator: Specify the record separator (e.g., as an octal value). Corresponds to -0[octal]. + line_terminator: Enable line ending processing and specify the line terminator. Corresponds to -l[octal]. + enable_warnings: Enable many useful warnings. Corresponds to -w. + enable_all_warnings: Enable all warnings. Corresponds to -W. + disable_all_warnings: Disable all warnings. Corresponds to -X. + enable_tainting_checks: Enable tainting checks for security. Corresponds to -T. + enable_tainting_warnings: Enable tainting warnings. Corresponds to -t. + allow_unsafe_operations: Allow unsafe operations. Corresponds to -U. + run_under_debugger: Run the program under the specified debugger. Corresponds to -d[:debugger]. + debugging_flags: Set debugging flags. Corresponds to -D[number/list]. + unicode_features: Enable the listed Unicode features. Corresponds to -C[number/list]. + no_sitecustomize: Don't do $sitelib/sitecustomize.pl at startup. Corresponds to -f. + enable_switch_parsing: Enable rudimentary parsing for switches after the program file. Corresponds to -s. + search_path_for_program: Look for the program file using the PATH environment variable. Corresponds to -S. + dump_core: Dump core after parsing the program. Corresponds to -u. + ignore_text_before_shebang_dir: Ignore text before #!perl line and optionally cd to the specified directory. Corresponds to -x[directory]. + + Returns: + A dictionary containing the command executed, stdout, stderr, and a list of output files. + """ + # --- Input Validation --- + if programfile and (execute_program or execute_program_features): + raise ValueError("Cannot specify both a 'programfile' and 'execute_program'/'execute_program_features'.") + + if not any([programfile, execute_program, execute_program_features, print_version, print_config_variable is not None]): + raise ValueError("Must provide a program to run (e.g., 'programfile' or 'execute_program') or an info flag (e.g., 'print_version').") + + if programfile and not programfile.exists(): + raise FileNotFoundError(f"The specified program file does not exist: {programfile}") + + if autosplit and not (loop_around_program or loop_and_print): + raise ValueError("Autosplit mode (-a) requires a loop flag (-n or -p) to be enabled.") + + if split_pattern and not autosplit: + raise ValueError("A split pattern (-F) can only be used with autosplit mode (-a).") + + if sum([enable_warnings, enable_all_warnings, disable_all_warnings]) > 1: + raise ValueError("Warning flags -w, -W, and -X are mutually exclusive.") + + # --- Command Construction --- + cmd = ["perl"] + + if record_separator is not None: + cmd.append(f"-0{record_separator}") + if autosplit: + cmd.append("-a") + if unicode_features is not None: + cmd.append(f"-C{unicode_features}") + if check_syntax_only: + cmd.append("-c") + if run_under_debugger is not None: + cmd.append(f"-d{run_under_debugger}") + if debugging_flags is not None: + cmd.append(f"-D{debugging_flags}") + if execute_program: + for prog_line in execute_program: + cmd.extend(["-e", prog_line]) + if execute_program_features: + for prog_line in execute_program_features: + cmd.extend(["-E", prog_line]) + if no_sitecustomize: + cmd.append("-f") + if split_pattern is not None: + cmd.append(f"-F{split_pattern}") + if in_place_edit_extension is not None: + cmd.append(f"-i{in_place_edit_extension}") + if include_directories: + for directory in include_directories: + cmd.extend(["-I", str(directory)]) + if line_terminator is not None: + cmd.append(f"-l{line_terminator}") + if use_module: + for mod in use_module: + cmd.extend(["-M", mod]) + if no_module: + for mod in no_module: + cmd.extend(["-m", mod]) + if loop_around_program: + cmd.append("-n") + if loop_and_print: + cmd.append("-p") + if enable_switch_parsing: + cmd.append("-s") + if search_path_for_program: + cmd.append("-S") + if enable_tainting_warnings: + cmd.append("-t") + if enable_tainting_checks: + cmd.append("-T") + if dump_core: + cmd.append("-u") + if allow_unsafe_operations: + cmd.append("-U") + if print_version: + cmd.append("-v") + if print_config_variable is not None: + cmd.append(f"-V{print_config_variable}") + if enable_warnings: + cmd.append("-w") + if enable_all_warnings: + cmd.append("-W") + if ignore_text_before_shebang_dir: + cmd.extend(["-x", str(ignore_text_before_shebang_dir)]) + if disable_all_warnings: + cmd.append("-X") + + if programfile: + cmd.append(str(programfile)) + + if arguments: + cmd.append("--") + cmd.extend(arguments) + + # --- Subprocess Execution --- + command_executed = shlex.join(cmd) + try: + result = subprocess.run( + cmd, + capture_output=True, + text=True, + check=True + ) + stdout = result.stdout + stderr = result.stderr + except FileNotFoundError: + return { + "command_executed": command_executed, + "stdout": "", + "stderr": "Error: 'perl' command not found. Please ensure Perl is installed and in your PATH.", + "output_files": [] + } + except subprocess.CalledProcessError as e: + return { + "command_executed": command_executed, + "stdout": e.stdout, + "stderr": e.stderr, + "output_files": [] + } + + # --- Structured Result Return --- + return { + "command_executed": command_executed, + "stdout": stdout, + "stderr": stderr, + "output_files": [] # The interpreter itself doesn't guarantee output files. + } \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-sereal/app/perl-sereal_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-sereal/app/perl-sereal_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..fc6d749d723c9c149f97b56a80a61a213f94a981 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-sereal/app/perl-sereal_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-sereal/app/perl-sereal_server.py') +SERVER_NAME = 'biosci_perl_sereal' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-sereal/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-sereal/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-sereal/app/requirements.txt @@ -0,0 +1 @@ + diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-sereal/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-sereal/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..17b091ee6a6e0de26c0dc58fd19826c191dfd0a2 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-sereal/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-perl-sereal: + build: . + image: mcp-perl-sereal:latest + container_name: mcp-perl-sereal + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=perl-sereal + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-sereal/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-sereal/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..72cebcb51617d918039b61e1eb78a8efaa052893 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-sereal/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - perl-sereal + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-sereal/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-sereal/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-sereal/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-set-scalar/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-set-scalar/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-set-scalar/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-sort-naturally/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-sort-naturally/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..8f39ce296e8a3c580c3cceebb69a85f41e6d8c54 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-sort-naturally/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - perl-sort-naturally + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-sort-naturally/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-sort-naturally/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-sort-naturally/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-sub-name/app/perl-sub-name_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-sub-name/app/perl-sub-name_server.py new file mode 100644 index 0000000000000000000000000000000000000000..219e29bc1a5290524152d6ba9603a982defdd824 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-sub-name/app/perl-sub-name_server.py @@ -0,0 +1,33 @@ +import subprocess +from typing import Dict + +# This is a placeholder. mcp is not a real library. +class mcp: + @staticmethod + def tool(): + def decorator(func): + return func + return decorator + +@mcp.tool() +def sub_name() -> Dict[str, str]: + """ + Note: `perl-sub-name` is a Perl library (`Sub::Name`) for assigning names to subroutines at runtime. + It does not provide a command-line executable. This MCP tool is a placeholder and does not + execute any command, as there is no underlying tool to run. + """ + # Since this is a library and not a CLI tool, we cannot execute it. + # The return value indicates that no command was run and provides an + # informative message in stderr. + + error_message = ( + "Error: perl-sub-name is a Perl library, not a command-line tool. " + "It cannot be executed directly." + ) + + return { + "command_executed": "", + "stdout": "", + "stderr": error_message, + "output_files": [] + } diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-sub-name/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-sub-name/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-sub-name/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-test-harness/app/perl-test-harness_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-test-harness/app/perl-test-harness_server.py new file mode 100644 index 0000000000000000000000000000000000000000..ec77f7df72ce073718e1cdd1c997b489387e05d3 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-test-harness/app/perl-test-harness_server.py @@ -0,0 +1,242 @@ +import subprocess +from pathlib import Path +from typing import List, Optional, Union + +@mcp.tool() +def run_perl_script( + program_file: Optional[Path] = None, + program_arguments: Optional[List[str]] = None, + program_code: Optional[List[str]] = None, + program_code_with_features: Optional[List[str]] = None, + zero_record_separator: Optional[str] = None, + autosplit: bool = False, + unicode_features: Optional[str] = None, + check_syntax_only: bool = False, + debugger: Union[bool, str] = False, + debugging_flags: Optional[str] = None, + no_site_customize: bool = False, + split_pattern: Optional[str] = None, + edit_in_place_extension: Optional[str] = None, + include_directories: Optional[List[Path]] = None, + line_ending_processing: Optional[str] = None, + use_modules: Optional[List[str]] = None, + no_modules: Optional[List[str]] = None, + assume_loop_n: bool = False, + assume_loop_p: bool = False, + rudimentary_switch_parsing: bool = False, + search_path_for_program: bool = False, + enable_tainting_warnings: bool = False, + enable_tainting_checks: bool = False, + dump_core_after_parsing: bool = False, + allow_unsafe_operations: bool = False, + print_version: bool = False, + print_config_summary: Union[bool, str] = False, + enable_warnings: bool = False, + enable_all_warnings: bool = False, + ignore_text_before_perl_line: Union[bool, Path] = False, + disable_all_warnings: bool = False, +) -> dict: + """ + Executes a Perl script or one-liner with various Perl interpreter options. + + This tool wraps the 'perl' command, allowing users to run Perl programs + with fine-grained control over its execution environment and features. + It supports running a script file, or providing Perl code directly via + one-liners (-e or -E). + + Args: + program_file: Path to the Perl script file to execute. + program_arguments: List of arguments to pass to the Perl script. + program_code: List of Perl code lines to execute directly via -e. + Can be specified multiple times for multiple lines. + program_code_with_features: List of Perl code lines to execute directly via -E, + enabling all optional features. Can be specified + multiple times for multiple lines. + zero_record_separator: Specify record separator. If an empty string, defaults to \\0. + Otherwise, specifies an octal value. + autosplit: Enable autosplit mode with -n or -p (splits $_ into @F). + unicode_features: Enables the listed Unicode features (number or list). + check_syntax_only: Check syntax only (runs BEGIN and CHECK blocks). + debugger: Run program under debugger. If True, runs with default debugger. + If a string, specifies the debugger (e.g., 'MyDebugger'). + debugging_flags: Set debugging flags (argument is a bit mask or alphabets). + no_site_customize: Don't do $sitelib/sitecustomize.pl at startup. + split_pattern: split() pattern for -a switch (e.g., '/\\s+/'). + edit_in_place_extension: Edit <> files in place. If an extension is supplied, + makes a backup (e.g., '.bak'). + include_directories: List of directories to specify for @INC/#include. + line_ending_processing: Enable line ending processing. If an octal value is + supplied, specifies the line terminator. If an empty + string, enables processing with default terminator. + use_modules: List of modules to execute "use module..." before executing program. + Uses the -M switch. + no_modules: List of modules to execute "no module..." before executing program. + Uses the -M- switch. + assume_loop_n: Assume "while (<>) { ... }" loop around program. + assume_loop_p: Assume loop like -n but print line also, like sed. + rudimentary_switch_parsing: Enable rudimentary parsing for switches after programfile. + search_path_for_program: Look for programfile using PATH environment variable. + enable_tainting_warnings: Enable tainting warnings. + enable_tainting_checks: Enable tainting checks. + dump_core_after_parsing: Dump core after parsing program. + allow_unsafe_operations: Allow unsafe operations. + print_version: Print Perl version, patchlevel, and license. + print_config_summary: Print configuration summary. If True, prints full summary. + If a string, prints a single Config.pm variable (e.g., 'archname'). + enable_warnings: Enable many useful warnings (-w). + enable_all_warnings: Enable all warnings (-W). + ignore_text_before_perl_line: Ignore text before #!perl line. If True, ignores. + If a Path, optionally cd to that directory. + disable_all_warnings: Disable all warnings (-X). + + Returns: + A dictionary containing the command executed, stdout, stderr, and any output files. + """ + cmd: List[str] = ["perl"] + output_files: List[Path] = [] + + # Input validation + if program_file and (program_code or program_code_with_features): + raise ValueError("Cannot specify both 'program_file' and 'program_code'/'program_code_with_features'.") + if not program_file and not program_code and not program_code_with_features and not print_version and not print_config_summary: + raise ValueError("Either 'program_file', 'program_code', 'program_code_with_features', 'print_version', or 'print_config_summary' must be provided.") + + if assume_loop_n and assume_loop_p: + raise ValueError("Cannot use both 'assume_loop_n' (-n) and 'assume_loop_p' (-p) simultaneously.") + + # Conflicting warning options + warning_options_count = sum([enable_warnings, enable_all_warnings, disable_all_warnings]) + if warning_options_count > 1: + raise ValueError("Cannot use conflicting warning options (-w, -W, -X) simultaneously.") + + if program_file: + if not program_file.is_file(): + raise FileNotFoundError(f"Program file not found: {program_file}") + + # Command construction + if zero_record_separator is not None: + cmd.append(f"-0{zero_record_separator}") + if autosplit: + cmd.append("-a") + if unicode_features is not None: + cmd.append(f"-C{unicode_features}") + if check_syntax_only: + cmd.append("-c") + if debugger: + if isinstance(debugger, str): + cmd.append(f"-d:{debugger}") + else: # debugger is True + cmd.append("-d") + if debugging_flags is not None: + cmd.append(f"-D{debugging_flags}") + if program_code: + for code_line in program_code: + cmd.append("-e") + cmd.append(code_line) + if program_code_with_features: + for code_line in program_code_with_features: + cmd.append("-E") + cmd.append(code_line) + if no_site_customize: + cmd.append("-f") + if split_pattern is not None: + cmd.append(f"-F{split_pattern}") + if edit_in_place_extension is not None: + cmd.append(f"-i{edit_in_place_extension}") + if include_directories: + for inc_dir in include_directories: + if not inc_dir.is_dir(): + raise NotADirectoryError(f"Include directory not found or not a directory: {inc_dir}") + cmd.append("-I") + cmd.append(str(inc_dir)) + if line_ending_processing is not None: + cmd.append(f"-l{line_ending_processing}") + if use_modules: + for module in use_modules: + cmd.append("-M") + cmd.append(module) + if no_modules: + for module in no_modules: + cmd.append("-M-") + cmd.append(module) + if assume_loop_n: + cmd.append("-n") + if assume_loop_p: + cmd.append("-p") + if rudimentary_switch_parsing: + cmd.append("-s") + if search_path_for_program: + cmd.append("-S") + if enable_tainting_warnings: + cmd.append("-t") + if enable_tainting_checks: + cmd.append("-T") + if dump_core_after_parsing: + cmd.append("-u") + if allow_unsafe_operations: + cmd.append("-U") + if print_version: + cmd.append("-v") + if print_config_summary: + if isinstance(print_config_summary, str): + cmd.append(f"-V:{print_config_summary}") + else: # print_config_summary is True + cmd.append("-V") + if enable_warnings: + cmd.append("-w") + if enable_all_warnings: + cmd.append("-W") + if ignore_text_before_perl_line: + if isinstance(ignore_text_before_perl_line, Path): + if not ignore_text_before_perl_line.is_dir(): + raise NotADirectoryError(f"Directory for -x option not found or not a directory: {ignore_text_before_perl_line}") + cmd.append(f"-x{ignore_text_before_perl_line}") + else: # ignore_text_before_perl_line is True + cmd.append("-x") + if disable_all_warnings: + cmd.append("-X") + + # Add program file and its arguments (positional) + if program_file: + cmd.append(str(program_file)) + if program_arguments: + cmd.extend(program_arguments) + + process = None + try: + process = subprocess.run( + cmd, + capture_output=True, + text=True, + check=True + ) + stdout = process.stdout + stderr = process.stderr + return { + "command_executed": " ".join(cmd), + "stdout": stdout, + "stderr": stderr, + "output_files": output_files, + } + except FileNotFoundError: + return { + "command_executed": " ".join(cmd), + "stdout": "", + "stderr": "Error: 'perl' command not found. Please ensure Perl is installed and in your PATH.", + "output_files": [], + } + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(cmd), + "stdout": e.stdout, + "stderr": e.stderr, + "output_files": [], + } + except Exception as e: + # Catch any other unexpected errors + return { + "command_executed": " ".join(cmd), + "stdout": process.stdout if process else "", + "stderr": f"An unexpected error occurred: {e}", + "output_files": [], + } \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-test-harness/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-test-harness/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-test-harness/app/requirements.txt @@ -0,0 +1 @@ + diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-test-harness/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-test-harness/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..98784a24433ea366bf5bba85b5f5ed6f15dfd6e4 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-test-harness/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-perl-test-harness: + build: . + image: mcp-perl-test-harness:latest + container_name: mcp-perl-test-harness + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=perl-test-harness + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-test-harness/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-test-harness/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..0291b8e5b4e6d9afb082581b367dcc5366516db7 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-test-harness/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - perl-test-harness + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-test-harness/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-test-harness/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-test-harness/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-test-most/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-test-most/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-test-most/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-test/app/perl-test_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-test/app/perl-test_server.py new file mode 100644 index 0000000000000000000000000000000000000000..315cc6b3efe834d0fa615d0b28509dbe20a20bbf --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-test/app/perl-test_server.py @@ -0,0 +1,155 @@ +import subprocess +from pathlib import Path +from typing import Optional, Dict + +# Assuming mcp is imported from the Model Context Protocol library +# e.g., from mcp import mcp + +@mcp.tool() +def perl_test( + input_file: Path, + output_file: Path, + threshold: float = 0.05, + iterations: int = 1000, + verbose: bool = False, + seed: Optional[int] = None, +) -> Dict: + """ + Runs a hypothetical 'perl-test' analysis. + + This tool simulates running a Perl-based bioinformatics test, taking an input file + and generating an output file based on specified parameters. + + Args: + input_file: Path to the input data file. + output_file: Path to write the output results. + threshold: A significance threshold for the test. Defaults to 0.05. + iterations: Number of iterations or permutations to run. Defaults to 1000. + verbose: If True, enables verbose logging. Defaults to False. + seed: An optional integer seed for reproducibility. Defaults to None. + + Returns: + A dictionary containing the execution details and output file path. + """ + # --- Input Validation --- + if not input_file.is_file(): + raise FileNotFoundError(f"Input file not found: {input_file}") + + if not (0.0 <= threshold <= 1.0): + raise ValueError("Threshold must be between 0.0 and 1.0.") + + if iterations <= 0: + raise ValueError("Iterations must be a positive integer.") + + if seed is not None and seed < 0: + raise ValueError("Seed must be a non-negative integer.") + + # Ensure the output directory exists + output_file.parent.mkdir(parents=True, exist_ok=True) + + # --- Command Construction --- + cmd = [ + "perl-test", + "--input", str(input_file), + "--output", str(output_file), + "--threshold", str(threshold), + "--iterations", str(iterations), + ] + + if verbose: + cmd.append("--verbose") + + if seed is not None: + cmd.extend(["--seed", str(seed)]) + + command_executed = " ".join(cmd) + + # --- Subprocess Execution --- + try: + result = subprocess.run( + cmd, + check=True, + capture_output=True, + text=True, + ) + return { + "command_executed": command_executed, + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": {"results": str(output_file)}, + } + except FileNotFoundError: + raise RuntimeError( + "The 'perl-test' command was not found. Please ensure it is installed and in your system's PATH." + ) + except subprocess.CalledProcessError as e: + # Return a structured error if the tool fails + return { + "command_executed": command_executed, + "stdout": e.stdout, + "stderr": e.stderr, + "error": f"perl-test failed with exit code {e.returncode}", + "output_files": {}, + } + + +@mcp.tool() +def perl_test_validate( + input_file: Path, + format: str = "fasta", +) -> Dict: + """ + Validates the format of an input file using the 'perl-test validate' subcommand. + + This tool checks if the input file conforms to a specified format like 'fasta' or 'fastq'. + + Args: + input_file: The file to validate. + format: The expected format (e.g., 'fasta', 'fastq', 'gff'). Defaults to 'fasta'. + + Returns: + A dictionary containing the execution details of the validation command. + """ + # --- Input Validation --- + if not input_file.is_file(): + raise FileNotFoundError(f"Input file not found: {input_file}") + + allowed_formats = ["fasta", "fastq", "gff", "vcf", "bed"] + if format.lower() not in allowed_formats: + raise ValueError( + f"Invalid format '{format}'. Allowed formats are: {', '.join(allowed_formats)}" + ) + + # --- Command Construction --- + cmd = [ + "perl-test", + "validate", + "--input", str(input_file), + "--format", format, + ] + command_executed = " ".join(cmd) + + # --- Subprocess Execution --- + try: + result = subprocess.run( + cmd, + check=True, + capture_output=True, + text=True, + ) + return { + "command_executed": command_executed, + "stdout": result.stdout, + "stderr": result.stderr, + } + except FileNotFoundError: + raise RuntimeError( + "The 'perl-test' command was not found. Please ensure it is installed and in your system's PATH." + ) + except subprocess.CalledProcessError as e: + return { + "command_executed": command_executed, + "stdout": e.stdout, + "stderr": e.stderr, + "error": f"perl-test validate failed with exit code {e.returncode}", + } \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-tree-dag_node/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-tree-dag_node/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..e89b37c88c979bd73af67f808385be53022703bc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-tree-dag_node/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install perl-tree-dag_node via conda (e.g., from bioconda) +RUN conda install -c bioconda perl-tree-dag_node -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY app/perl-tree-dag_node_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/perl-tree-dag_node_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/perl-tree-dag_node_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-tree-dag_node/app/perl-tree-dag_node_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-tree-dag_node/app/perl-tree-dag_node_server.py new file mode 100644 index 0000000000000000000000000000000000000000..8b83d7f56a453cc0bde2b31b7881031b198e9c6e --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-tree-dag_node/app/perl-tree-dag_node_server.py @@ -0,0 +1,270 @@ +from typing import Optional, List, Dict, Any +import subprocess +from pathlib import Path +import json + +@mcp.tool() +def perl_run_script( + script_path: str, + args: Optional[List[str]] = None, + warnings: bool = False, + all_warnings: bool = False, + include_dirs: Optional[List[str]] = None, + modules: Optional[List[str]] = None, + inplace_extension: Optional[str] = None, + record_separator: Optional[str] = None, + unicode_features: Optional[str] = None, + use_all_features: bool = False, + taint_checks: bool = False, +): + """ + Execute a Perl script using the perl interpreter with various switches. + + :param script_path: Path to the Perl program file. + :param args: Arguments to pass to the Perl script. + :param warnings: Enable many useful warnings (-w). + :param all_warnings: Enable all warnings (-W). + :param include_dirs: Specify @INC/#include directories (-I). + :param modules: Execute "use module..." before executing program (-M). + :param inplace_extension: Edit <> files in place, optionally making a backup (-i). + :param record_separator: Specify record separator (-0). + :param unicode_features: Enable listed Unicode features (-C). + :param use_all_features: Enable all optional features (-E). + :param taint_checks: Enable tainting checks (-T). + """ + path_obj = Path(script_path) + if not path_obj.exists(): + return {"error": f"Script file not found: {script_path}"} + + command = ["perl"] + + if warnings: + command.append("-w") + if all_warnings: + command.append("-W") + if use_all_features: + command.append("-E") + if taint_checks: + command.append("-T") + if include_dirs: + for d in include_dirs: + command.append(f"-I{d}") + if modules: + for m in modules: + command.append(f"-M{m}") + if record_separator: + command.append(f"-0{record_separator}") + if unicode_features: + command.append(f"-C{unicode_features}") + if inplace_extension: + command.append(f"-i{inplace_extension}") + + command.append(str(path_obj)) + + if args: + command.extend(args) + + try: + result = subprocess.run(command, capture_output=True, text=True, check=True) + return { + "command_executed": " ".join(command), + "stdout": result.stdout, + "stderr": result.stderr, + "returncode": result.returncode + } + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(command), + "stdout": e.stdout, + "stderr": e.stderr, + "returncode": e.returncode, + "error": str(e) + } + +@mcp.tool() +def perl_run_inline( + code: str, + args: Optional[List[str]] = None, + warnings: bool = False, + all_warnings: bool = False, + include_dirs: Optional[List[str]] = None, + modules: Optional[List[str]] = None, + autosplit: bool = False, + split_pattern: Optional[str] = None, + loop_files: bool = False, + loop_and_print: bool = False, + record_separator: Optional[str] = None, + unicode_features: Optional[str] = None, + use_all_features: bool = False, +): + """ + Execute inline Perl code using the -e or -E flag. + + :param code: One line of program (several -e's allowed, but here provided as one block). + :param args: Arguments to pass to the inline program. + :param warnings: Enable many useful warnings (-w). + :param all_warnings: Enable all warnings (-W). + :param include_dirs: Specify @INC/#include directories (-I). + :param modules: Execute "use module..." before executing program (-M). + :param autosplit: Autosplit mode with -n or -p (splits $_ into @F) (-a). + :param split_pattern: split() pattern for -a switch (-F). + :param loop_files: Assume "while (<>) { ... }" loop around program (-n). + :param loop_and_print: Assume loop like -n but print line also (-p). + :param record_separator: Specify record separator (-0). + :param unicode_features: Enable listed Unicode features (-C). + :param use_all_features: Use -E instead of -e to enable all optional features. + """ + command = ["perl"] + + if warnings: + command.append("-w") + if all_warnings: + command.append("-W") + if autosplit: + command.append("-a") + if loop_files: + command.append("-n") + if loop_and_print: + command.append("-p") + if include_dirs: + for d in include_dirs: + command.append(f"-I{d}") + if modules: + for m in modules: + command.append(f"-M{m}") + if split_pattern: + command.append(f"-F{split_pattern}") + if record_separator: + command.append(f"-0{record_separator}") + if unicode_features: + command.append(f"-C{unicode_features}") + + if use_all_features: + command.extend(["-E", code]) + else: + command.extend(["-e", code]) + + if args: + command.extend(args) + + try: + result = subprocess.run(command, capture_output=True, text=True, check=True) + return { + "command_executed": " ".join(command), + "stdout": result.stdout, + "stderr": result.stderr, + "returncode": result.returncode + } + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(command), + "stdout": e.stdout, + "stderr": e.stderr, + "returncode": e.returncode, + "error": str(e) + } + +@mcp.tool() +def perl_check_syntax(script_path: str): + """ + Check the syntax of a Perl script without executing it (-c). + + :param script_path: Path to the Perl program file. + """ + path_obj = Path(script_path) + if not path_obj.exists(): + return {"error": f"Script file not found: {script_path}"} + + command = ["perl", "-c", str(path_obj)] + + try: + result = subprocess.run(command, capture_output=True, text=True, check=True) + return { + "command_executed": " ".join(command), + "stdout": result.stdout, + "stderr": result.stderr, + "status": "Syntax OK" + } + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(command), + "stdout": e.stdout, + "stderr": e.stderr, + "status": "Syntax Error", + "error": str(e) + } + +@mcp.tool() +def tree_dag_node_visualize(lol_definition: str): + """ + Visualize an N-ary tree using the Tree::DAG_Node module. + + :param lol_definition: A Perl 'List of Lists' string representing the tree structure. + Example: "['root', ['child1'], ['child2', ['grandchild']]]" + The format is: [node_name, [child1_lol], [child2_lol], ...] + """ + # Construct a Perl script that uses Tree::DAG_Node to render the ASCII tree + perl_code = ( + "use Tree::DAG_Node; " + f"my $lol = {lol_definition}; " + "my $root = Tree::DAG_Node->lol_to_tree($lol); " + "print $root->draw_ascii_tree;" + ) + + command = ["perl", "-MTree::DAG_Node", "-e", perl_code] + + try: + result = subprocess.run(command, capture_output=True, text=True, check=True) + return { + "command_executed": " ".join(command), + "ascii_tree": result.stdout, + "stderr": result.stderr + } + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(command), + "stdout": e.stdout, + "stderr": e.stderr, + "error": "Failed to render tree. Ensure lol_definition is a valid Perl List-of-Lists and Tree::DAG_Node is installed." + } + +@mcp.tool() +def tree_dag_node_version(): + """ + Get the version of the installed Tree::DAG_Node Perl module. + """ + command = ["perl", "-MTree::DAG_Node", "-e", "print $Tree::DAG_Node::VERSION"] + + try: + result = subprocess.run(command, capture_output=True, text=True, check=True) + return { + "command_executed": " ".join(command), + "version": result.stdout.strip() + } + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(command), + "error": "Tree::DAG_Node module not found or perl execution failed.", + "stderr": e.stderr + } + +@mcp.tool() +def tree_dag_node_check_dependencies(): + """ + Check if the required Perl dependencies for Tree::DAG_Node are installed. + """ + deps = ["Tree::DAG_Node", "File::Slurper", "ExtUtils::MakeMaker"] + results = {} + + for dep in deps: + cmd = ["perl", f"-M{dep}", "-e", "1"] + try: + subprocess.run(cmd, capture_output=True, check=True) + results[dep] = "Installed" + except subprocess.CalledProcessError: + results[dep] = "Missing" + + return { + "dependencies_status": results, + "all_satisfied": all(v == "Installed" for v in results.values()) + } \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-tree-dag_node/app/perl-tree-dag_node_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-tree-dag_node/app/perl-tree-dag_node_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..6cfabf07009e6b25f89542e24fc3af43d54ba6c1 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-tree-dag_node/app/perl-tree-dag_node_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-tree-dag_node/app/perl-tree-dag_node_server.py') +SERVER_NAME = 'biosci_perl_tree_dag_node' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-tree-dag_node/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-tree-dag_node/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-tree-dag_node/app/requirements.txt @@ -0,0 +1 @@ + diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-tree-dag_node/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-tree-dag_node/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..8f29388842807424e721a6f4c4eec048f00f667b --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-tree-dag_node/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-perl-tree-dag_node: + build: . + image: mcp-perl-tree-dag_node:latest + container_name: mcp-perl-tree-dag_node + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=perl-tree-dag_node + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-tree-dag_node/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-tree-dag_node/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..f3d70a34ebdfb9b2ff2a069b3e4d01d3721c5232 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-tree-dag_node/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - perl-tree-dag_node + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-tree-dag_node/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-tree-dag_node/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-tree-dag_node/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-types-serialiser/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-types-serialiser/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-types-serialiser/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-xml-namespacesupport/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-xml-namespacesupport/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..64e41c86c971ea103bcf1564da14eb4f139de2f2 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-xml-namespacesupport/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install perl-xml-namespacesupport via conda (e.g., from bioconda) +RUN conda install -c bioconda perl-xml-namespacesupport -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY app/perl-xml-namespacesupport_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/perl-xml-namespacesupport_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/perl-xml-namespacesupport_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-xml-namespacesupport/app/perl-xml-namespacesupport_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-xml-namespacesupport/app/perl-xml-namespacesupport_server.py new file mode 100644 index 0000000000000000000000000000000000000000..5b6f0e18967dc77020c4bc1e498755fd8f18634d --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-xml-namespacesupport/app/perl-xml-namespacesupport_server.py @@ -0,0 +1,83 @@ +import subprocess +from pathlib import Path +from typing import Optional, Dict, Any + +# No need to import mcp as per instructions + +@mcp.tool() +def get_module_info() -> Dict[str, Any]: + """ + Provides information about the perl-xml-namespacesupport module. + + This tool is designed to interact with the perl-xml-namespacesupport module, + which is primarily a Perl library for programmatic use, not a standalone + command-line executable. This function attempts to verify the module's + presence and retrieve its version by executing a Perl one-liner. + + As perl-xml-namespacesupport does not expose direct command-line functions, + this tool serves as a utility to confirm its installation and basic status. + + Returns: + A dictionary containing the command executed, stdout, stderr, and any + output files (which will be empty in this case). + """ + command = ["perl", "-MXML::NamespaceSupport", "-e", "print $XML::NamespaceSupport::VERSION // 'VERSION not defined'"] + command_str = " ".join(command) + stdout = "" + stderr = "" + output_files: list[str] = [] + + try: + result = subprocess.run(command, capture_output=True, text=True, check=True) + stdout = result.stdout.strip() + stderr = result.stderr.strip() + + # Check for module not found in stderr, even if check=True didn't catch it (less likely with -M) + if "Can't locate XML/NamespaceSupport.pm" in stderr: + return { + "command_executed": command_str, + "stdout": stdout, + "stderr": f"Error: Perl module XML::NamespaceSupport not found. Please ensure it is installed in your Perl environment. Original stderr: {stderr}", + "output_files": output_files, + } + + except subprocess.CalledProcessError as e: + stdout = e.stdout.strip() + stderr = e.stderr.strip() + + if "Can't locate XML/NamespaceSupport.pm" in stderr: + return { + "command_executed": command_str, + "stdout": stdout, + "stderr": f"Error: Perl module XML::NamespaceSupport not found. Please ensure it is installed in your Perl environment. Original stderr: {stderr}", + "output_files": output_files, + } + else: + return { + "command_executed": command_str, + "stdout": stdout, + "stderr": f"Error executing Perl command (exit code {e.returncode}): {e}\n{stderr}", + "output_files": output_files, + } + except FileNotFoundError: + return { + "command_executed": command_str, + "stdout": "", + "stderr": "Error: 'perl' command not found. Please ensure Perl is installed and accessible in your PATH.", + "output_files": output_files, + } + except Exception as e: + # Catch any other unexpected errors during execution + return { + "command_executed": command_str, + "stdout": stdout, + "stderr": f"An unexpected error occurred: {e}\n{stderr}", + "output_files": output_files, + } + + return { + "command_executed": command_str, + "stdout": stdout, + "stderr": stderr, + "output_files": output_files, + } \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-xml-namespacesupport/app/perl-xml-namespacesupport_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-xml-namespacesupport/app/perl-xml-namespacesupport_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..e9cad2ab0a5fac7b4e66713ee53e9a7369a03f15 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-xml-namespacesupport/app/perl-xml-namespacesupport_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-xml-namespacesupport/app/perl-xml-namespacesupport_server.py') +SERVER_NAME = 'biosci_perl_xml_namespacesupport' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-xml-namespacesupport/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-xml-namespacesupport/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-xml-namespacesupport/app/requirements.txt @@ -0,0 +1 @@ + diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-xml-namespacesupport/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-xml-namespacesupport/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..e4a75cea631affd21a341310dbc648e7faadb29f --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-xml-namespacesupport/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-perl-xml-namespacesupport: + build: . + image: mcp-perl-xml-namespacesupport:latest + container_name: mcp-perl-xml-namespacesupport + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=perl-xml-namespacesupport + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-xml-namespacesupport/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-xml-namespacesupport/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..d71e545f2f858462b13761cfadcfc84ade648af4 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-xml-namespacesupport/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - perl-xml-namespacesupport + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-xml-namespacesupport/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-xml-namespacesupport/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-xml-namespacesupport/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-xml-regexp/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-xml-regexp/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..0c9f1c0ce8551eb32942d5cf70a37f4153c474e6 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-xml-regexp/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install perl-xml-regexp via conda (e.g., from bioconda) +RUN conda install -c bioconda perl-xml-regexp -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY app/perl-xml-regexp_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/perl-xml-regexp_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/perl-xml-regexp_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-xml-regexp/app/perl-xml-regexp_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-xml-regexp/app/perl-xml-regexp_server.py new file mode 100644 index 0000000000000000000000000000000000000000..4a9a6fb39b243c1f4812f7b1511a083092885aca --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-xml-regexp/app/perl-xml-regexp_server.py @@ -0,0 +1,162 @@ +import subprocess +from pathlib import Path +from typing import List, Optional, Dict, Any + +@mcp.tool() +def perl_xml_regexp_validate( + text: str, + token_type: str = "Name" +) -> Dict[str, Any]: + """ + Validates if a given string matches a specific XML token regular expression + provided by the XML::RegExp Perl module. + + Args: + text: The string to validate against the XML regular expression. + token_type: The type of XML token to validate against. + Common values: 'Name', 'NmToken', 'NCName', 'QName', 'AttValue', + 'EntityValue', 'SystemLiteral', 'PubidLiteral', 'Char', 'S', + 'VersionNum', 'EncName', 'EncodingDecl', 'VersionInfo', 'Misc', + 'Comment', 'PI', 'CDSect', 'Reference', 'EntityRef', 'CharRef'. + """ + # List of common valid tokens in XML::RegExp for validation + valid_tokens = [ + "Name", "NmToken", "NCName", "QName", "AttValue", "EntityValue", + "SystemLiteral", "PubidLiteral", "Char", "S", "VersionNum", + "EncName", "EncodingDecl", "VersionInfo", "Misc", "Comment", + "PI", "CDSect", "Reference", "EntityRef", "CharRef", "BaseChar", + "Ideographic", "CombiningChar", "Digit", "Extender", "Letter" + ] + + if token_type not in valid_tokens: + # We allow it but warn or handle it; Perl will throw an error if the variable doesn't exist + pass + + # Construct the perl command + # We use -MXML::RegExp to load the module + # We use $ARGV[0] for the text and $ARGV[1] for the token type to avoid shell injection + perl_script = 'use XML::RegExp; my $type = $ARGV[1]; no strict "refs"; if ($ARGV[0] =~ /^${"XML::RegExp::$type"}$/) { print "MATCH"; } else { print "NO_MATCH"; }' + + cmd = [ + "perl", + "-MXML::RegExp", + "-e", perl_script, + text, + token_type + ] + + try: + result = subprocess.run(cmd, capture_output=True, text=True, check=True) + is_valid = result.stdout.strip() == "MATCH" + + return { + "command_executed": " ".join(cmd), + "is_valid": is_valid, + "token_type": token_type, + "input_text": text, + "stdout": result.stdout, + "stderr": result.stderr + } + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(cmd), + "error": str(e), + "stdout": e.stdout, + "stderr": e.stderr + } + +@mcp.tool() +def perl_xml_regexp_execute( + code: str, + args: Optional[List[str]] = None +) -> Dict[str, Any]: + """ + Executes a custom Perl snippet that utilizes the XML::RegExp module. + The module is automatically loaded via '-MXML::RegExp'. + + Args: + code: The Perl code to execute. Example: 'print $XML::RegExp::Name' + args: Optional list of command-line arguments to pass to the Perl script. + """ + cmd_args = args if args is not None else [] + + cmd = [ + "perl", + "-MXML::RegExp", + "-e", code, + "--" + ] + cmd_args + + try: + result = subprocess.run(cmd, capture_output=True, text=True, check=True) + return { + "command_executed": " ".join(cmd), + "stdout": result.stdout, + "stderr": result.stderr, + "status": "success" + } + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(cmd), + "error": str(e), + "stdout": e.stdout, + "stderr": e.stderr, + "status": "error" + } + +@mcp.tool() +def perl_xml_regexp_info() -> Dict[str, Any]: + """ + Returns the version information of the installed XML::RegExp module and the Perl interpreter. + """ + perl_cmd = ["perl", "-v"] + module_cmd = ["perl", "-MXML::RegExp", "-e", "print $XML::RegExp::VERSION"] + + try: + perl_ver = subprocess.run(perl_cmd, capture_output=True, text=True, check=True) + mod_ver = subprocess.run(module_cmd, capture_output=True, text=True, check=True) + + return { + "perl_version_info": perl_ver.stdout.splitlines()[1] if len(perl_ver.stdout.splitlines()) > 1 else perl_ver.stdout, + "xml_regexp_version": mod_ver.stdout.strip(), + "status": "success" + } + except subprocess.CalledProcessError as e: + return { + "error": "Failed to retrieve version information. Ensure perl and perl-xml-regexp are installed.", + "stderr": e.stderr, + "status": "error" + } + +@mcp.tool() +def perl_xml_regexp_list_regexes() -> Dict[str, Any]: + """ + Lists the available regular expression variables provided by the XML::RegExp module. + """ + # This script inspects the XML::RegExp namespace + perl_script = ( + 'use XML::RegExp; ' + 'foreach my $entry (sort keys %XML::RegExp::) { ' + ' print "$entry\\n" if $entry =~ /^[A-Z]/; ' + '}' + ) + + cmd = ["perl", "-MXML::RegExp", "-e", perl_script] + + try: + result = subprocess.run(cmd, capture_output=True, text=True, check=True) + regex_list = [line.strip() for line in result.stdout.splitlines() if line.strip()] + + return { + "available_regex_variables": regex_list, + "command_executed": " ".join(cmd), + "stdout": result.stdout, + "stderr": result.stderr + } + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(cmd), + "error": str(e), + "stdout": e.stdout, + "stderr": e.stderr + } \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-xml-regexp/app/perl-xml-regexp_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-xml-regexp/app/perl-xml-regexp_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..8f740fd6df437e9bbdf7dfc608721a25769877ab --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-xml-regexp/app/perl-xml-regexp_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-xml-regexp/app/perl-xml-regexp_server.py') +SERVER_NAME = 'biosci_perl_xml_regexp' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-xml-regexp/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-xml-regexp/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-xml-regexp/app/requirements.txt @@ -0,0 +1 @@ + diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-xml-regexp/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-xml-regexp/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..c92fc9c45f722c087cc45f134c8cbdac7bb09b16 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-xml-regexp/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-perl-xml-regexp: + build: . + image: mcp-perl-xml-regexp:latest + container_name: mcp-perl-xml-regexp + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=perl-xml-regexp + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-xml-regexp/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-xml-regexp/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..c28e608c0cf6a96dd35c8a6cca6c05b2e28a69c2 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-xml-regexp/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - perl-xml-regexp + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-xml-regexp/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-xml-regexp/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-xml-regexp/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-xml-xpathengine/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-xml-xpathengine/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..a4eda5f369b8be494368ba55ea269bafbdf53589 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-xml-xpathengine/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install perl-xml-xpathengine via conda (e.g., from bioconda) +RUN conda install -c bioconda perl-xml-xpathengine -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY app/perl-xml-xpathengine_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/perl-xml-xpathengine_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/perl-xml-xpathengine_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-xml-xpathengine/app/perl-xml-xpathengine_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-xml-xpathengine/app/perl-xml-xpathengine_server.py new file mode 100644 index 0000000000000000000000000000000000000000..61c7c8b0cc551fa3f3fe4efc9d08f6823042996d --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-xml-xpathengine/app/perl-xml-xpathengine_server.py @@ -0,0 +1,230 @@ +import subprocess +from pathlib import Path +from typing import List, Optional, Union + +# No need to import mcp as per instructions + +@mcp.tool() +def run_perl_script( + program_file: Optional[Path] = None, + inline_program: Optional[List[str]] = None, + inline_program_extended: Optional[List[str]] = None, + program_arguments: Optional[List[str]] = None, + record_separator_octal: Optional[str] = None, + autosplit_mode: bool = False, + unicode_features: Optional[str] = None, + check_syntax_only: bool = False, + debugger: Optional[str] = None, + debugging_flags: Optional[str] = None, + no_sitecustomize: bool = False, + split_pattern: Optional[str] = None, + edit_in_place_extension: Optional[str] = None, + include_directories: Optional[List[Path]] = None, + line_ending_processing: Optional[str] = None, + use_modules: Optional[List[str]] = None, + no_use_modules: Optional[List[str]] = None, + loop_around_program: bool = False, + loop_and_print: bool = False, + rudimentary_switch_parsing: bool = False, + search_path_for_program: bool = False, + tainting_warnings: bool = False, + tainting_checks: bool = False, + dump_core: bool = False, + allow_unsafe_operations: bool = False, + print_version: bool = False, + print_config_summary: Optional[str] = None, + enable_warnings: bool = False, + enable_all_warnings: bool = False, + ignore_text_before_shebang_dir: Optional[Path] = None, + disable_all_warnings: bool = False, +) -> dict: + """ + Executes a Perl script with specified interpreter options. + + This tool provides access to the command-line options of the Perl interpreter. + The 'perl-xml-xpathengine' package is a Perl module and does not have its + own standalone command-line interface. This tool allows running a generic + Perl script, which can then internally 'use XML::XPathEngine;' to utilize + its functionality. + + Args: + program_file: Path to the Perl script file to execute. Required if no inline program is provided. + inline_program: One or more lines of Perl program code to execute directly. + Can be specified multiple times via a list. Corresponds to -e. + inline_program_extended: One or more lines of Perl program code, like `inline_program`, + but enables all optional features. Can be specified multiple times via a list. + Corresponds to -E. + program_arguments: Arguments to be passed to the Perl script itself. + record_separator_octal: Specify record separator (e.g., '0' for NUL, '0777' for paragraph mode). + If no argument, defaults to '\0'. Corresponds to -0[octal]. + autosplit_mode: Enable autosplit mode with -n or -p (splits $_ into @F). Corresponds to -a. + unicode_features: Enables listed Unicode features (e.g., 'all', 'io', 'say'). Corresponds to -C[number/list]. + check_syntax_only: Check syntax only (runs BEGIN and CHECK blocks). Corresponds to -c. + debugger: Run program under debugger. Optionally specify debugger name (e.g., 'MyDebugger'). + Corresponds to -d[:debugger]. + debugging_flags: Set debugging flags (argument is a bit mask or alphabets). Corresponds to -D[number/list]. + no_sitecustomize: Don't do $sitelib/sitecustomize.pl at startup. Corresponds to -f. + split_pattern: Pattern for split() function used with -a switch (e.g., '/\\s+/'). + Slashes are optional. Corresponds to -F/pattern/. + edit_in_place_extension: Edit <> files in place. If extension is supplied (e.g., '.bak'), + makes a backup. If empty string, no backup. Corresponds to -i[extension]. + include_directories: Specify @INC/#include directories. Can be specified multiple times. + Corresponds to -Idirectory. + line_ending_processing: Enable line ending processing. Optionally specifies line terminator + (e.g., '012' for newline). Corresponds to -l[octal]. + use_modules: List of modules to execute "use module..." before executing the program. + Corresponds to -Mmodule. + no_use_modules: List of modules to execute "no module..." before executing the program. + Corresponds to -M-module. + loop_around_program: Assume "while (<>) { ... }" loop around program. Corresponds to -n. + loop_and_print: Assume loop like -n but print line also, like sed. Corresponds to -p. + rudimentary_switch_parsing: Enable rudimentary parsing for switches after programfile. Corresponds to -s. + search_path_for_program: Look for programfile using PATH environment variable. Corresponds to -S. + tainting_warnings: Enable tainting warnings. Corresponds to -t. + tainting_checks: Enable tainting checks. Corresponds to -T. + dump_core: Dump core after parsing program. Corresponds to -u. + allow_unsafe_operations: Allow unsafe operations. Corresponds to -U. + print_version: Print Perl version, patchlevel, and license. Corresponds to -v. + print_config_summary: Print configuration summary. Optionally specify a single Config.pm variable. + Corresponds to -V[:variable]. + enable_warnings: Enable many useful warnings (-w). Corresponds to -w. + enable_all_warnings: Enable all warnings (-W). Corresponds to -W. + ignore_text_before_shebang_dir: Ignore text before #!perl line. Optionally change to directory. + If an empty Path is provided (Path('')), it corresponds to -x. + Corresponds to -x[directory]. + disable_all_warnings: Disable all warnings (-X). Corresponds to -X. + """ + cmd = ["perl"] + output_files = [] + + # Input validation + if program_file and not program_file.is_file(): + raise FileNotFoundError(f"Program file not found: {program_file}") + if program_file and (inline_program or inline_program_extended): + raise ValueError("Cannot specify both a program file and inline program code (-e/-E).") + if not program_file and not (inline_program or inline_program_extended): + raise ValueError("Either a program file or inline program code (-e/-E) must be provided.") + + if loop_around_program and loop_and_print: + raise ValueError("Cannot use both -n (loop_around_program) and -p (loop_and_print) simultaneously.") + + if enable_warnings and enable_all_warnings: + raise ValueError("Cannot use both -w (enable_warnings) and -W (enable_all_warnings) simultaneously.") + if (enable_warnings or enable_all_warnings) and disable_all_warnings: + raise ValueError("Cannot enable warnings (-w or -W) and disable all warnings (-X) simultaneously.") + + # Add switches + if record_separator_octal is not None: + cmd.append(f"-0{record_separator_octal}") + if autosplit_mode: + cmd.append("-a") + if unicode_features is not None: + cmd.append(f"-C{unicode_features}") + if check_syntax_only: + cmd.append("-c") + if debugger is not None: + cmd.append(f"-d:{debugger}" if debugger else "-d") + if debugging_flags is not None: + cmd.append(f"-D{debugging_flags}") + if inline_program: + for prog_line in inline_program: + cmd.extend(["-e", prog_line]) + if inline_program_extended: + for prog_line in inline_program_extended: + cmd.extend(["-E", prog_line]) + if no_sitecustomize: + cmd.append("-f") + if split_pattern is not None: + cmd.append(f"-F{split_pattern}") + if edit_in_place_extension is not None: + cmd.append(f"-i{edit_in_place_extension}") + if include_directories: + for inc_dir in include_directories: + if not inc_dir.is_dir(): + raise FileNotFoundError(f"Include directory not found: {inc_dir}") + cmd.extend(["-I", str(inc_dir)]) + if line_ending_processing is not None: + cmd.append(f"-l{line_ending_processing}") + if use_modules: + for module in use_modules: + cmd.append(f"-M{module}") # Using -M as it's more common for CLI + if no_use_modules: + for module in no_use_modules: + cmd.append(f"-M-{module}") # Using -M- + if loop_around_program: + cmd.append("-n") + if loop_and_print: + cmd.append("-p") + if rudimentary_switch_parsing: + cmd.append("-s") + if search_path_for_program: + cmd.append("-S") + if tainting_warnings: + cmd.append("-t") + if tainting_checks: + cmd.append("-T") + if dump_core: + cmd.append("-u") + if allow_unsafe_operations: + cmd.append("-U") + if print_version: + cmd.append("-v") + if print_config_summary is not None: + cmd.append(f"-V:{print_config_summary}" if print_config_summary else "-V") + if enable_warnings: + cmd.append("-w") + if enable_all_warnings: + cmd.append("-W") + if ignore_text_before_shebang_dir is not None: + # If Path('') is provided, it means -x with no directory, which is valid. + # If a specific directory is provided, check if it exists. + if str(ignore_text_before_shebang_dir) and not ignore_text_before_shebang_dir.is_dir(): + raise FileNotFoundError(f"Directory for -x not found: {ignore_text_before_shebang_dir}") + cmd.append(f"-x{ignore_text_before_shebang_dir}") + if disable_all_warnings: + cmd.append("-X") + + # Add program file + if program_file: + cmd.append(str(program_file)) + # If only inline programs are given, no program_file is needed. + # The -e/-E options handle the script directly. + + # Add program arguments + if program_arguments: + cmd.extend(program_arguments) + + try: + process = subprocess.run( + [str(arg) for arg in cmd], # Ensure all args are strings + capture_output=True, + text=True, + check=True + ) + stdout = process.stdout + stderr = process.stderr + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join([str(arg) for arg in cmd]), + "stdout": e.stdout, + "stderr": e.stderr, + "error": str(e), + "returncode": e.returncode, + "output_files": output_files, + } + except FileNotFoundError: + return { + "command_executed": " ".join([str(arg) for arg in cmd]), + "stdout": "", + "stderr": "Error: 'perl' command not found. Is Perl installed and in your PATH?", + "error": "Perl executable not found.", + "returncode": 127, + "output_files": output_files, + } + + return { + "command_executed": " ".join([str(arg) for arg in cmd]), + "stdout": stdout, + "stderr": stderr, + "output_files": output_files, + } \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-xml-xpathengine/app/perl-xml-xpathengine_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-xml-xpathengine/app/perl-xml-xpathengine_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..2f08a4f58d853a1081f5b226dedc88f8fab83d67 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-xml-xpathengine/app/perl-xml-xpathengine_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-xml-xpathengine/app/perl-xml-xpathengine_server.py') +SERVER_NAME = 'biosci_perl_xml_xpathengine' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-xml-xpathengine/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-xml-xpathengine/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-xml-xpathengine/app/requirements.txt @@ -0,0 +1 @@ + diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-xml-xpathengine/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-xml-xpathengine/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..fb30a2be498036e2af16b5c118a4ad237c713b8d --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-xml-xpathengine/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-perl-xml-xpathengine: + build: . + image: mcp-perl-xml-xpathengine:latest + container_name: mcp-perl-xml-xpathengine + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=perl-xml-xpathengine + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-xml-xpathengine/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-xml-xpathengine/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..036ce7b25811ffa1335ae16f30dbc724344b3175 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-xml-xpathengine/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - perl-xml-xpathengine + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-xml-xpathengine/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-xml-xpathengine/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-xml-xpathengine/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-yaml/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-yaml/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..9f5a7a5ee239375dcb74afc06a9aba79e54661d5 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-yaml/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install perl-yaml via conda (e.g., from bioconda) +RUN conda install -c bioconda perl-yaml -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY app/perl-yaml_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/perl-yaml_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/perl-yaml_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-yaml/app/perl-yaml_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-yaml/app/perl-yaml_server.py new file mode 100644 index 0000000000000000000000000000000000000000..73b68ddd106ff2d7d929cb0417d5de5e0e8928bb --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-yaml/app/perl-yaml_server.py @@ -0,0 +1,290 @@ +import subprocess +import json +from pathlib import Path +from typing import Optional, List, Dict, Any, Union + +def _run_perl_cmd(args: List[str], stdin: Optional[str] = None) -> Dict[str, Any]: + """Helper function to execute perl commands and return structured output.""" + cmd = ["perl"] + args + try: + result = subprocess.run( + cmd, + input=stdin, + capture_output=True, + text=True, + check=True + ) + return { + "command_executed": " ".join(cmd), + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": [] + } + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(cmd), + "stdout": e.stdout, + "stderr": e.stderr, + "error": f"Command failed with return code {e.returncode}" + } + +@mcp.tool() +def yaml_dump( + json_data: str, + indent: int = 2, + sort_keys: bool = True, + use_header: bool = True, + use_version: bool = False, + compress_series: bool = True, + stringify: bool = False, + numify: bool = False, + quote_numeric_strings: bool = False, + anchor_prefix: str = "", +) -> Dict[str, Any]: + """ + Turn Perl data structures (passed as JSON) into a YAML stream using YAML::Dump. + + Args: + json_data: JSON string representing the data structure(s) to serialize. + indent: Number of space characters to use for each indentation level (default 2). + sort_keys: Whether to sort hash keys alphabetically (default True). + use_header: Whether to include the '---' separator string (default True). + use_version: Whether to include the YAML version on the header (default False). + compress_series: Whether to compress the formatting of arrays of hashes (default True). + stringify: Honor string overloading for objects (default False). + numify: Values that look like numbers will be numified when loaded (default False). + quote_numeric_strings: Force quoting of strings that resemble numbers (default False). + anchor_prefix: String to prepend to each anchor number. + """ + if indent < 0: + indent = 2 + + perl_script = ( + "use YAML; use JSON::PP; " + f"$YAML::Indent = {indent}; " + f"$YAML::SortKeys = {1 if sort_keys else 0}; " + f"$YAML::UseHeader = {1 if use_header else 0}; " + f"$YAML::UseVersion = {1 if use_version else 0}; " + f"$YAML::CompressSeries = {1 if compress_series else 0}; " + f"$YAML::Stringify = {1 if stringify else 0}; " + f"$YAML::Numify = {1 if numify else 0}; " + f"$YAML::QuoteNumericStrings = {1 if quote_numeric_strings else 0}; " + f"$YAML::AnchorPrefix = '{anchor_prefix}'; " + "my $json_text = do { local $/; }; " + "my $data = decode_json($json_text); " + "if (ref($data) eq 'ARRAY') { print Dump(@$data); } else { print Dump($data); }" + ) + + return _run_perl_cmd(["-e", perl_script], stdin=json_data) + +@mcp.tool() +def yaml_load( + yaml_stream: str, + load_blessed: bool = False, + numify: bool = False, +) -> Dict[str, Any]: + """ + Turn a YAML stream into Perl data structures (returned as JSON) using YAML::Load. + + Args: + yaml_stream: The YAML content string to parse. + load_blessed: When true, YAML nodes with special tags will be automatically blessed into objects. + numify: Values that look like numbers will be numified when loaded. + """ + perl_script = ( + "use YAML; use JSON::PP; " + f"$YAML::LoadBlessed = {1 if load_blessed else 0}; " + f"$YAML::Numify = {1 if numify else 0}; " + "my $yaml_text = do { local $/; }; " + "my @docs = Load($yaml_text); " + "print encode_json(\\@docs);" + ) + + return _run_perl_cmd(["-e", perl_script], stdin=yaml_stream) + +@mcp.tool() +def yaml_dump_file( + filepath: str, + json_data: str, + indent: int = 2, + sort_keys: bool = True, +) -> Dict[str, Any]: + """ + Writes a YAML stream to a file using YAML::DumpFile. + + Args: + filepath: Path to the output YAML file. + json_data: JSON string representing the data to serialize. + indent: Number of spaces for each indentation level. + sort_keys: Whether to sort hash keys. + """ + path = Path(filepath) + if not path.parent.exists(): + return {"error": f"Parent directory does not exist: {path.parent}"} + + perl_script = ( + "use YAML; use JSON::PP; " + f"$YAML::Indent = {indent}; " + f"$YAML::SortKeys = {1 if sort_keys else 0}; " + "my $file = shift; " + "my $json_text = do { local $/; }; " + "my $data = decode_json($json_text); " + "if (ref($data) eq 'ARRAY') { DumpFile($file, @$data); } else { DumpFile($file, $data); }" + ) + + result = _run_perl_cmd(["-e", perl_script, str(path)], stdin=json_data) + if "error" not in result: + result["output_files"] = [str(path)] + return result + +@mcp.tool() +def yaml_load_file( + filepath: str, + load_blessed: bool = False, +) -> Dict[str, Any]: + """ + Reads a YAML stream from a file using YAML::LoadFile. + + Args: + filepath: Path to the input YAML file. + load_blessed: Whether to load blessed objects. + """ + path = Path(filepath) + if not path.exists(): + return {"error": f"File not found: {filepath}"} + + perl_script = ( + "use YAML; use JSON::PP; " + f"$YAML::LoadBlessed = {1 if load_blessed else 0}; " + "my $file = shift; " + "my @docs = LoadFile($file); " + "print encode_json(\\@docs);" + ) + + return _run_perl_cmd(["-e", perl_script, str(path)]) + +@mcp.tool() +def yaml_freeze( + json_data: str, +) -> Dict[str, Any]: + """ + Alias to Dump(). Turn Perl data into YAML. + + Args: + json_data: JSON string representing the data. + """ + perl_script = "use YAML; use JSON::PP; print freeze(decode_json(do { local $/; }));" + return _run_perl_cmd(["-e", perl_script], stdin=json_data) + +@mcp.tool() +def yaml_thaw( + yaml_stream: str, +) -> Dict[str, Any]: + """ + Alias to Load(). Turn YAML into Perl data. + + Args: + yaml_stream: The YAML content to parse. + """ + perl_script = "use YAML; use JSON::PP; print encode_json([thaw(do { local $/; })]);" + return _run_perl_cmd(["-e", perl_script], stdin=yaml_stream) + +@mcp.tool() +def yaml_bless( + json_data: str, + class_name: str, +) -> Dict[str, Any]: + """ + Associate a Perl data structure with a class name for YAML serialization using YAML::Bless. + + Args: + json_data: JSON string representing the data. + class_name: The class name to bless the data into. + """ + perl_script = ( + "use YAML; use JSON::PP; " + "my $json = do { local $/; }; " + "my $data = decode_json($json); " + f"Bless($data, '{class_name}'); " + "print Dump($data);" + ) + return _run_perl_cmd(["-e", perl_script], stdin=json_data) + +@mcp.tool() +def perl_cli( + one_liner: str = "", + program_file: Optional[str] = None, + arguments: Optional[List[str]] = None, + autosplit: bool = False, + check_syntax: bool = False, + warnings: bool = False, + all_warnings: bool = False, + no_warnings: bool = False, + include_dirs: Optional[List[str]] = None, + modules: Optional[List[str]] = None, + inplace_extension: Optional[str] = None, + assume_loop_n: bool = False, + assume_loop_p: bool = False, + version: bool = False, +) -> Dict[str, Any]: + """ + Execute the Perl interpreter with various switches. + + Args: + one_liner: One line of program (equivalent to -e). + program_file: Path to a perl script file to execute. + arguments: Arguments passed to the program. + autosplit: Enable autosplit mode with -n or -p (equivalent to -a). + check_syntax: Check syntax only (equivalent to -c). + warnings: Enable many useful warnings (equivalent to -w). + all_warnings: Enable all warnings (equivalent to -W). + no_warnings: Disable all warnings (equivalent to -X). + include_dirs: Directories to search for modules (equivalent to -I). + modules: Modules to execute 'use module' before the program (equivalent to -m). + inplace_extension: Edit files in place (equivalent to -i). + assume_loop_n: Assume 'while (<>) { ... }' loop (equivalent to -n). + assume_loop_p: Assume loop like -n but print line (equivalent to -p). + version: Print version and patchlevel (equivalent to -v). + """ + cmd_args = [] + if autosplit: cmd_args.append("-a") + if check_syntax: cmd_args.append("-c") + if warnings: cmd_args.append("-w") + if all_warnings: cmd_args.append("-W") + if no_warnings: cmd_args.append("-X") + if assume_loop_n: cmd_args.append("-n") + if assume_loop_p: cmd_args.append("-p") + if version: cmd_args.append("-v") + + if include_dirs: + for d in include_dirs: + cmd_args.append(f"-I{d}") + + if modules: + for m in modules: + cmd_args.append(f"-m{m}") + + if inplace_extension is not None: + cmd_args.append(f"-i{inplace_extension}") + + if one_liner: + cmd_args.extend(["-e", one_liner]) + + if program_file: + p = Path(program_file) + if not p.exists(): + return {"error": f"Program file not found: {program_file}"} + cmd_args.append(str(p)) + + if arguments: + cmd_args.extend(arguments) + + return _run_perl_cmd(cmd_args) + +@mcp.tool() +def perl_yaml_version() -> Dict[str, Any]: + """ + Print the version of the YAML module and the Perl interpreter. + """ + perl_script = "use YAML; print 'YAML version: ' . $YAML::VERSION . \"\\n\";" + return _run_perl_cmd(["-e", perl_script]) \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-yaml/app/perl-yaml_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-yaml/app/perl-yaml_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..3ea29a2948434e8c644f6c9935461ad9a01aa5da --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-yaml/app/perl-yaml_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-yaml/app/perl-yaml_server.py') +SERVER_NAME = 'biosci_perl_yaml' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-yaml/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-yaml/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-yaml/app/requirements.txt @@ -0,0 +1 @@ + diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-yaml/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-yaml/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..9cc13a8511fb6aef79c053dc44e1c88ce0f1142d --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-yaml/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-perl-yaml: + build: . + image: mcp-perl-yaml:latest + container_name: mcp-perl-yaml + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=perl-yaml + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-yaml/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-yaml/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..a5d44d3c424ed030a2ea49f57c26e2d3090ece12 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-yaml/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - perl-yaml + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-yaml/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-yaml/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_perl-yaml/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_portcullis/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_portcullis/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..a1c48d470334f96d83cd33502e6a74b9d0e9ea0d --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_portcullis/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install portcullis via conda (e.g., from bioconda) +RUN conda install -c bioconda portcullis -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY app/portcullis_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/portcullis_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/portcullis_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_portcullis/app/portcullis_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_portcullis/app/portcullis_server.py new file mode 100644 index 0000000000000000000000000000000000000000..84400781ce5aa7e58935157008d647ef3cbafa84 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_portcullis/app/portcullis_server.py @@ -0,0 +1,369 @@ +import subprocess +from pathlib import Path +from typing import Optional, List, Union + +@mcp.tool() +def portcullis_full( + bam_file: str, + reference: str, + output_dir: str = "portcullis_out", + threads: int = 1, + force: bool = False, + bamfilt: bool = False, + extra_args: Optional[List[str]] = None, +): + """ + Runs the complete Portcullis pipeline: prep, junc, filter, and optionally bamfilt. + + Args: + bam_file: Path to the input BAM file. + reference: Path to the reference genome (FASTA). + output_dir: Directory to store output files. + threads: Number of threads to use. + force: Force overwrite of existing output directory. + bamfilt: If True, also runs the bamfilt step to produce a filtered BAM. + extra_args: Additional command line arguments for portcullis full. + """ + bam_path = Path(bam_file) + ref_path = Path(reference) + if not bam_path.exists(): + return {"error": f"BAM file not found: {bam_file}"} + if not ref_path.exists(): + return {"error": f"Reference file not found: {reference}"} + + cmd = ["portcullis", "full"] + cmd.extend(["--output", output_dir]) + cmd.extend(["--reference", reference]) + cmd.extend(["--threads", str(threads)]) + + if force: + cmd.append("--force") + if bamfilt: + cmd.append("--bamfilt") + if extra_args: + cmd.extend(extra_args) + + cmd.append(bam_file) + + try: + result = subprocess.run(cmd, capture_output=True, text=True, check=True) + return { + "command_executed": " ".join(cmd), + "stdout": result.stdout, + "stderr": result.stderr, + "output_directory": output_dir + } + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(cmd), + "error": str(e), + "stdout": e.stdout, + "stderr": e.stderr + } + +@mcp.tool() +def portcullis_prep( + bam_file: str, + output_dir: str = "portcullis_prep_out", + threads: int = 1, + force: bool = False, +): + """ + Prepares input data (BAM) so that it is suitable for junction analysis. + + Args: + bam_file: Path to the input BAM file. + output_dir: Directory to store prepared data. + threads: Number of threads to use. + force: Force overwrite of existing output directory. + """ + if not Path(bam_file).exists(): + return {"error": f"BAM file not found: {bam_file}"} + + cmd = ["portcullis", "prep", "--output", output_dir, "--threads", str(threads)] + if force: + cmd.append("--force") + cmd.append(bam_file) + + try: + result = subprocess.run(cmd, capture_output=True, text=True, check=True) + return { + "command_executed": " ".join(cmd), + "stdout": result.stdout, + "stderr": result.stderr, + "output_directory": output_dir + } + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(cmd), + "error": str(e), + "stdout": e.stdout, + "stderr": e.stderr + } + +@mcp.tool() +def portcullis_junc( + input_data: str, + reference: str, + output_dir: str = "portcullis_junc_out", + threads: int = 1, + force: bool = False, +): + """ + Calculates junction metrics for the prepared data or BAM file. + + Args: + input_data: Path to the input BAM file or the directory generated by 'prep'. + reference: Path to the reference genome (FASTA). + output_dir: Directory to store junction metrics. + threads: Number of threads to use. + force: Force overwrite of existing output directory. + """ + if not Path(input_data).exists(): + return {"error": f"Input data path not found: {input_data}"} + if not Path(reference).exists(): + return {"error": f"Reference file not found: {reference}"} + + cmd = ["portcullis", "junc", "--output", output_dir, "--reference", reference, "--threads", str(threads)] + if force: + cmd.append("--force") + cmd.append(input_data) + + try: + result = subprocess.run(cmd, capture_output=True, text=True, check=True) + return { + "command_executed": " ".join(cmd), + "stdout": result.stdout, + "stderr": result.stderr, + "output_directory": output_dir + } + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(cmd), + "error": str(e), + "stdout": e.stdout, + "stderr": e.stderr + } + +@mcp.tool() +def portcullis_filter( + input_dir: str, + output_dir: str = "portcullis_filter_out", + threads: int = 1, + force: bool = False, +): + """ + Separates alignments based on whether they are likely to represent genuine splice junctions or not. + + Args: + input_dir: Path to the directory containing junction metrics (output from 'junc'). + output_dir: Directory to store filtered results. + threads: Number of threads to use. + force: Force overwrite of existing output directory. + """ + if not Path(input_dir).exists(): + return {"error": f"Input directory not found: {input_dir}"} + + cmd = ["portcullis", "filter", "--output", output_dir, "--threads", str(threads)] + if force: + cmd.append("--force") + cmd.append(input_dir) + + try: + result = subprocess.run(cmd, capture_output=True, text=True, check=True) + return { + "command_executed": " ".join(cmd), + "stdout": result.stdout, + "stderr": result.stderr, + "output_directory": output_dir + } + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(cmd), + "error": str(e), + "stdout": e.stdout, + "stderr": e.stderr + } + +@mcp.tool() +def portcullis_bamfilt( + bam_file: str, + junction_file: str, + output_bam: str = "filtered.bam", + threads: int = 1, +): + """ + Filters a BAM file to remove any reads associated with invalid junctions. + + Args: + bam_file: Path to the original BAM file. + junction_file: Path to the filtered junctions file (usually .pass.junctions.bed). + output_bam: Path to the output filtered BAM file. + threads: Number of threads to use. + """ + if not Path(bam_file).exists(): + return {"error": f"BAM file not found: {bam_file}"} + if not Path(junction_file).exists(): + return {"error": f"Junction file not found: {junction_file}"} + + cmd = ["portcullis", "bamfilt", "--output", output_bam, "--threads", str(threads), bam_file, junction_file] + + try: + result = subprocess.run(cmd, capture_output=True, text=True, check=True) + return { + "command_executed": " ".join(cmd), + "stdout": result.stdout, + "stderr": result.stderr, + "output_file": output_bam + } + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(cmd), + "error": str(e), + "stdout": e.stdout, + "stderr": e.stderr + } + +@mcp.tool() +def junctools_convert( + input_file: str, + output_file: str, + input_format: str = "bed", + output_format: str = "portcullis", +): + """ + Converts junction files between different formats using junctools. + + Args: + input_file: Path to the input junction file. + output_file: Path to the output junction file. + input_format: Format of the input file (e.g., bed, portcullis, tophat). + output_format: Format of the output file. + """ + if not Path(input_file).exists(): + return {"error": f"Input file not found: {input_file}"} + + cmd = ["junctools", "convert", "-i", input_format, "-o", output_format, input_file, output_file] + + try: + result = subprocess.run(cmd, capture_output=True, text=True, check=True) + return { + "command_executed": " ".join(cmd), + "stdout": result.stdout, + "stderr": result.stderr, + "output_file": output_file + } + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(cmd), + "error": str(e), + "stdout": e.stdout, + "stderr": e.stderr + } + +@mcp.tool() +def junctools_merge( + input_files: List[str], + output_file: str, + force: bool = False, +): + """ + Merges multiple junction files into a single file using junctools. + + Args: + input_files: List of paths to junction files to merge. + output_file: Path to the output merged junction file. + force: Force overwrite of existing output file. + """ + for f in input_files: + if not Path(f).exists(): + return {"error": f"Input file not found: {f}"} + + cmd = ["junctools", "merge"] + if force: + cmd.append("--force") + cmd.extend(["--output", output_file]) + cmd.extend(input_files) + + try: + result = subprocess.run(cmd, capture_output=True, text=True, check=True) + return { + "command_executed": " ".join(cmd), + "stdout": result.stdout, + "stderr": result.stderr, + "output_file": output_file + } + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(cmd), + "error": str(e), + "stdout": e.stdout, + "stderr": e.stderr + } + +@mcp.tool() +def junctools_compare( + file1: str, + file2: str, + output_prefix: str = "comparison", +): + """ + Compares two junction files using junctools. + + Args: + file1: Path to the first junction file. + file2: Path to the second junction file. + output_prefix: Prefix for output comparison files. + """ + if not Path(file1).exists(): + return {"error": f"File 1 not found: {file1}"} + if not Path(file2).exists(): + return {"error": f"File 2 not found: {file2}"} + + cmd = ["junctools", "compare", "--output", output_prefix, file1, file2] + + try: + result = subprocess.run(cmd, capture_output=True, text=True, check=True) + return { + "command_executed": " ".join(cmd), + "stdout": result.stdout, + "stderr": result.stderr, + "output_prefix": output_prefix + } + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(cmd), + "error": str(e), + "stdout": e.stdout, + "stderr": e.stderr + } + +@mcp.tool() +def junctools_stats( + input_file: str, +): + """ + Generates statistics for a junction file using junctools. + + Args: + input_file: Path to the junction file. + """ + if not Path(input_file).exists(): + return {"error": f"Input file not found: {input_file}"} + + cmd = ["junctools", "stats", input_file] + + try: + result = subprocess.run(cmd, capture_output=True, text=True, check=True) + return { + "command_executed": " ".join(cmd), + "stdout": result.stdout, + "stderr": result.stderr + } + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(cmd), + "error": str(e), + "stdout": e.stdout, + "stderr": e.stderr + } \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_portcullis/app/portcullis_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_portcullis/app/portcullis_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..d79052f19d8de7ae0b50f1ac0b47351f64d7baba --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_portcullis/app/portcullis_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_portcullis/app/portcullis_server.py') +SERVER_NAME = 'biosci_portcullis' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_portcullis/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_portcullis/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_portcullis/app/requirements.txt @@ -0,0 +1 @@ + diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_portcullis/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_portcullis/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..bcaec395a500d4fb54e93b67c206040366f75216 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_portcullis/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-portcullis: + build: . + image: mcp-portcullis:latest + container_name: mcp-portcullis + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=portcullis + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_portcullis/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_portcullis/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..81a2a55f6586b2834b82c059ad87957ddcb6f140 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_portcullis/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - portcullis + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_portcullis/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_portcullis/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_portcullis/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_pysam/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_pysam/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..98e2906ffe37b2b0cce9c4a1c76860067ff3ad97 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_pysam/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install pysam via conda (e.g., from bioconda) +RUN conda install -c bioconda pysam -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY app/pysam_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/pysam_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/pysam_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_pysam/app/pysam_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_pysam/app/pysam_server.py new file mode 100644 index 0000000000000000000000000000000000000000..e511e4954231ebf7643209890bd67f42faacf6c6 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_pysam/app/pysam_server.py @@ -0,0 +1,455 @@ +from pathlib import Path +import subprocess +from typing import Optional, List +import tempfile + +@mcp.tool() +def samtools_view( + input_file: str, + output_file: Optional[str] = None, + is_bam: bool = False, + is_cram: bool = False, + uncompressed: bool = False, + header_only: bool = False, + with_header: bool = False, + count: bool = False, + threads: int = 1, + reference_fasta: Optional[str] = None, + region: Optional[str] = None, + subsample_fraction: float = 0.0, +): + """ + Views and converts SAM/BAM/CRAM files using samtools view. + + :param input_file: Path to the input SAM, BAM, or CRAM file. + :param output_file: Path to the output file. + :param is_bam: Output in BAM format (-b). + :param is_cram: Output in CRAM format (-C). + :param uncompressed: Output uncompressed BAM (-u). + :param header_only: Output only the header (-H). + :param with_header: Include header in the output (-h). + :param count: Instead of printing the alignments, only count them (-c). + :param threads: Number of additional threads to use (-@). + :param reference_fasta: Reference sequence file for CRAM (-T). + :param region: Optional region specification (e.g., 'chr1:100-200'). + :param subsample_fraction: Fraction of templates/reads to subsample (-s). + """ + input_path = Path(input_file) + if not input_path.exists(): + return {"error": f"Input file {input_file} does not exist."} + + cmd = ["samtools", "view"] + + if is_bam: cmd.append("-b") + if is_cram: cmd.append("-C") + if uncompressed: cmd.append("-u") + if header_only: cmd.append("-H") + if with_header: cmd.append("-h") + if count: cmd.append("-c") + if threads > 1: cmd.extend(["-@", str(threads)]) + if reference_fasta: cmd.extend(["-T", reference_fasta]) + if subsample_fraction > 0.0: cmd.extend(["-s", str(subsample_fraction)]) + if output_file: cmd.extend(["-o", output_file]) + + cmd.append(input_file) + if region: + cmd.append(region) + + try: + result = subprocess.run(cmd, capture_output=True, text=True, check=True) + return { + "command_executed": " ".join(cmd), + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": [output_file] if output_file else [] + } + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(cmd), + "error": e.stderr, + "stdout": e.stdout + } + +@mcp.tool() +def samtools_sort( + input_file: str, + output_file: str, + threads: int = 1, + memory_per_thread: str = "768M", + sort_by_name: bool = False, + compression_level: int = -1, +): + """ + Sorts alignment files using samtools sort. + + :param input_file: Path to the input BAM/SAM/CRAM file. + :param output_file: Path to the sorted output file. + :param threads: Number of threads to use (-@). + :param memory_per_thread: Maximum memory per thread; suffix K/M/G recognized (-m). + :param sort_by_name: Sort by read name instead of chromosomal coordinates (-n). + :param compression_level: Set compression level, from 0 (uncompressed) to 9 (best) (-l). + """ + input_path = Path(input_file) + if not input_path.exists(): + return {"error": f"Input file {input_file} does not exist."} + + cmd = ["samtools", "sort", "-o", output_file] + if threads > 1: cmd.extend(["-@", str(threads)]) + if memory_per_thread != "768M": cmd.extend(["-m", memory_per_thread]) + if sort_by_name: cmd.append("-n") + if compression_level >= 0: cmd.extend(["-l", str(compression_level)]) + + cmd.append(input_file) + + try: + result = subprocess.run(cmd, capture_output=True, text=True, check=True) + return { + "command_executed": " ".join(cmd), + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": [output_file] + } + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(cmd), + "error": e.stderr + } + +@mcp.tool() +def samtools_index( + input_file: str, + csi: bool = False, + min_shift: int = 0, + threads: int = 1, +): + """ + Indexes a coordinate-sorted BAM or CRAM file. + + :param input_file: Path to the sorted BAM/CRAM file. + :param csi: Generate CSI index instead of BAI (-c). + :param min_shift: Set minimum interval size for CSI indices to 2^INT (-m). + :param threads: Number of threads to use (-@). + """ + input_path = Path(input_file) + if not input_path.exists(): + return {"error": f"Input file {input_file} does not exist."} + + cmd = ["samtools", "index"] + if csi: cmd.append("-c") + if min_shift > 0: cmd.extend(["-m", str(min_shift)]) + if threads > 1: cmd.extend(["-@", str(threads)]) + cmd.append(input_file) + + try: + result = subprocess.run(cmd, capture_output=True, text=True, check=True) + ext = ".csi" if csi else ".bai" + return { + "command_executed": " ".join(cmd), + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": [f"{input_file}{ext}"] + } + except subprocess.CalledProcessError as e: + return {"command_executed": " ".join(cmd), "error": e.stderr} + +@mcp.tool() +def samtools_faidx( + fasta_file: str, + regions: Optional[List[str]] = None, + output_file: Optional[str] = None, +): + """ + Indexes or extracts regions from a FASTA file. + + :param fasta_file: Path to the FASTA file. + :param regions: List of regions to extract (e.g., ['chr1:1-100']). + :param output_file: Path to the output file for extracted regions. + """ + fasta_path = Path(fasta_file) + if not fasta_path.exists(): + return {"error": f"Fasta file {fasta_file} does not exist."} + + cmd = ["samtools", "faidx", fasta_file] + if regions: + cmd.extend(regions) + if output_file: + cmd.extend(["-o", output_file]) + + try: + result = subprocess.run(cmd, capture_output=True, text=True, check=True) + return { + "command_executed": " ".join(cmd), + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": [output_file] if output_file else [f"{fasta_file}.fai"] + } + except subprocess.CalledProcessError as e: + return {"command_executed": " ".join(cmd), "error": e.stderr} + +@mcp.tool() +def samtools_flagstat( + input_file: str, + threads: int = 1, +): + """ + Provides simple statistics on BAM file flags. + + :param input_file: Path to the BAM/SAM/CRAM file. + :param threads: Number of threads to use (-@). + """ + if not Path(input_file).exists(): + return {"error": f"Input file {input_file} does not exist."} + + cmd = ["samtools", "flagstat"] + if threads > 1: cmd.extend(["-@", str(threads)]) + cmd.append(input_file) + + try: + result = subprocess.run(cmd, capture_output=True, text=True, check=True) + return { + "command_executed": " ".join(cmd), + "stdout": result.stdout, + "stderr": result.stderr + } + except subprocess.CalledProcessError as e: + return {"command_executed": " ".join(cmd), "error": e.stderr} + +@mcp.tool() +def bcftools_view( + input_file: str, + output_file: Optional[str] = None, + output_type: str = "v", + include_header: bool = True, + header_only: bool = False, + samples: Optional[str] = None, + regions: Optional[str] = None, + threads: int = 1, +): + """ + VCF/BCF conversion, subsetting, and filtering. + + :param input_file: Path to the VCF or BCF file. + :param output_file: Path to the output file. + :param output_type: Output type: 'b' (compressed BCF), 'u' (uncompressed BCF), 'z' (compressed VCF), 'v' (uncompressed VCF). + :param include_header: Include header in output (default True). + :param header_only: Output only the header (-H). + :param samples: Comma-separated list of samples to include/exclude (-s). + :param regions: Comma-separated list of regions (chr:from-to) (-r). + :param threads: Number of output compression threads (-@). + """ + if not Path(input_file).exists(): + return {"error": f"Input file {input_file} does not exist."} + + cmd = ["bcftools", "view"] + if output_type in ['b', 'u', 'z', 'v']: + cmd.extend(["-O", output_type]) + if not include_header: cmd.append("-h") + if header_only: cmd.append("-H") + if samples: cmd.extend(["-s", samples]) + if regions: cmd.extend(["-r", regions]) + if threads > 1: cmd.extend(["-@", str(threads)]) + if output_file: cmd.extend(["-o", output_file]) + + cmd.append(input_file) + + try: + result = subprocess.run(cmd, capture_output=True, text=True, check=True) + return { + "command_executed": " ".join(cmd), + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": [output_file] if output_file else [] + } + except subprocess.CalledProcessError as e: + return {"command_executed": " ".join(cmd), "error": e.stderr} + +@mcp.tool() +def bcftools_index( + input_file: str, + tbi: bool = False, + csi: bool = False, + threads: int = 1, +): + """ + Indexes VCF or BCF files. + + :param input_file: Path to the VCF/BCF file. + :param tbi: Generate TBI-format index for VCF files (-t). + :param csi: Generate CSI-format index (-c). + :param threads: Number of threads to use (-@). + """ + if not Path(input_file).exists(): + return {"error": f"Input file {input_file} does not exist."} + + cmd = ["bcftools", "index"] + if tbi: cmd.append("-t") + if csi: cmd.append("-c") + if threads > 1: cmd.extend(["-@", str(threads)]) + cmd.append(input_file) + + try: + result = subprocess.run(cmd, capture_output=True, text=True, check=True) + return { + "command_executed": " ".join(cmd), + "stdout": result.stdout, + "stderr": result.stderr + } + except subprocess.CalledProcessError as e: + return {"command_executed": " ".join(cmd), "error": e.stderr} + +@mcp.tool() +def bcftools_stats( + input_file: str, + fasta_ref: Optional[str] = None, + samples: Optional[str] = None, +): + """ + Parses VCF/BCF and produces stats. + + :param input_file: Path to the VCF/BCF file. + :param fasta_ref: Reference FASTA file for checking REF alleles (-f). + :param samples: Comma-separated list of samples or '-' for all samples (-s). + """ + if not Path(input_file).exists(): + return {"error": f"Input file {input_file} does not exist."} + + cmd = ["bcftools", "stats"] + if fasta_ref: cmd.extend(["-f", fasta_ref]) + if samples: cmd.extend(["-s", samples]) + cmd.append(input_file) + + try: + result = subprocess.run(cmd, capture_output=True, text=True, check=True) + return { + "command_executed": " ".join(cmd), + "stdout": result.stdout, + "stderr": result.stderr + } + except subprocess.CalledProcessError as e: + return {"command_executed": " ".join(cmd), "error": e.stderr} + +@mcp.tool() +def tabix_index( + input_file: str, + preset: str = "vcf", + sequence_col: int = 0, + begin_col: int = 0, + end_col: int = 0, + force: bool = False, +): + """ + Indexes tab-separated files using tabix. + + :param input_file: Path to the file to index (must be bgzipped). + :param preset: Preset format: gff, bed, sam, vcf (-p). + :param sequence_col: Column of sequence name (-s). + :param begin_col: Column of start position (-b). + :param end_col: Column of end position (-e). + :param force: Overwrite existing index (-f). + """ + if not Path(input_file).exists(): + return {"error": f"Input file {input_file} does not exist."} + + cmd = ["tabix"] + if force: cmd.append("-f") + if sequence_col > 0: cmd.extend(["-s", str(sequence_col)]) + if begin_col > 0: cmd.extend(["-b", str(begin_col)]) + if end_col > 0: cmd.extend(["-e", str(end_col)]) + if preset: cmd.extend(["-p", preset]) + + cmd.append(input_file) + + try: + result = subprocess.run(cmd, capture_output=True, text=True, check=True) + return { + "command_executed": " ".join(cmd), + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": [f"{input_file}.tbi"] + } + except subprocess.CalledProcessError as e: + return {"command_executed": " ".join(cmd), "error": e.stderr} + +@mcp.tool() +def samtools_mpileup( + input_files: List[str], + fasta_ref: str, + output_file: Optional[str] = None, + region: Optional[str] = None, + min_mq: int = 0, + min_bq: int = 13, +): + """ + Generates VCF, BCF or pileup for one or multiple BAM files. + + :param input_files: List of input BAM/CRAM files. + :param fasta_ref: Reference FASTA file (-f). + :param output_file: Output file name (-o). + :param region: Region to process (-r). + :param min_mq: Minimum mapping quality for an alignment to be used (-q). + :param min_bq: Minimum base quality for a base to be considered (-Q). + """ + for f in input_files: + if not Path(f).exists(): + return {"error": f"Input file {f} does not exist."} + if not Path(fasta_ref).exists(): + return {"error": f"Reference file {fasta_ref} does not exist."} + + cmd = ["samtools", "mpileup", "-f", fasta_ref] + if region: cmd.extend(["-r", region]) + if min_mq > 0: cmd.extend(["-q", str(min_mq)]) + if min_bq != 13: cmd.extend(["-Q", str(min_bq)]) + if output_file: cmd.extend(["-o", output_file]) + + cmd.extend(input_files) + + try: + result = subprocess.run(cmd, capture_output=True, text=True, check=True) + return { + "command_executed": " ".join(cmd), + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": [output_file] if output_file else [] + } + except subprocess.CalledProcessError as e: + return {"command_executed": " ".join(cmd), "error": e.stderr} + +@mcp.tool() +def samtools_depth( + input_files: List[str], + output_file: Optional[str] = None, + region: Optional[str] = None, + min_mq: int = 0, + min_bq: int = 0, +): + """ + Computes the depth of coverage at each position or region. + + :param input_files: List of input BAM/CRAM files. + :param output_file: Output file name (-o). + :param region: Region to process (-r). + :param min_mq: Minimum mapping quality (-q). + :param min_bq: Minimum base quality (-Q). + """ + for f in input_files: + if not Path(f).exists(): + return {"error": f"Input file {f} does not exist."} + + cmd = ["samtools", "depth"] + if region: cmd.extend(["-r", region]) + if min_mq > 0: cmd.extend(["-q", str(min_mq)]) + if min_bq > 0: cmd.extend(["-Q", str(min_bq)]) + if output_file: cmd.extend(["-o", output_file]) + + cmd.extend(input_files) + + try: + result = subprocess.run(cmd, capture_output=True, text=True, check=True) + return { + "command_executed": " ".join(cmd), + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": [output_file] if output_file else [] + } + except subprocess.CalledProcessError as e: + return {"command_executed": " ".join(cmd), "error": e.stderr} \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_pysam/app/pysam_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_pysam/app/pysam_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..622af51400fe04a5e3b4f32df4dac6f8f41f6609 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_pysam/app/pysam_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_pysam/app/pysam_server.py') +SERVER_NAME = 'biosci_pysam' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_pysam/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_pysam/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_pysam/app/requirements.txt @@ -0,0 +1 @@ + diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_pysam/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_pysam/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..71b8ee8c84ecc7182bbab4b448433d6124b6dd9f --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_pysam/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-pysam: + build: . + image: mcp-pysam:latest + container_name: mcp-pysam + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=pysam + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_pysam/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_pysam/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..f7d01d06245202176608dec75ffc87795ff9b5b8 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_pysam/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - pysam + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_pysam/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_pysam/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_pysam/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_pysftp/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_pysftp/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..06d2de73bc29cb7fffb060f858d0dd136098a38a --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_pysftp/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install pysftp via conda (e.g., from bioconda) +RUN conda install -c bioconda pysftp -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY app/pysftp_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/pysftp_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/pysftp_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_pysftp/app/pysftp_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_pysftp/app/pysftp_server.py new file mode 100644 index 0000000000000000000000000000000000000000..670dd66018519cfc7f6147ba35bfd7a9772d7e30 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_pysftp/app/pysftp_server.py @@ -0,0 +1,434 @@ +import subprocess +import os +from pathlib import Path +from typing import Optional, List, Dict, Any + +@mcp.tool() +def pysftp_get( + host: str, + username: str, + remote_path: str, + local_path: str, + password: Optional[str] = None, + port: int = 22, + private_key: Optional[str] = None, + private_key_pass: Optional[str] = None, + no_host_key_check: bool = False +) -> Dict[str, Any]: + """ + Download a file from a remote SFTP server using pysftp. + + Args: + host: Remote hostname or IP address. + username: SSH username. + remote_path: Path to the file on the remote server. + local_path: Path where the file should be saved locally. + password: SSH password (optional if using private key). + port: SSH port (default 22). + private_key: Path to a private key file (optional). + private_key_pass: Password for the private key (optional). + no_host_key_check: If True, bypasses host key verification (insecure). + """ + # Validate local path directory exists + local_file = Path(local_path) + if not local_file.parent.exists(): + return {"error": f"Local directory {local_file.parent} does not exist."} + + # Construct Python script to execute pysftp logic + # We use a subprocess to ensure the environment's pysftp is used and to handle the connection lifecycle + py_script = f""" +import pysftp +import sys + +cnopts = pysftp.CnOpts() +if {no_host_key_check}: + cnopts.hostkeys = None + +try: + with pysftp.Connection( + host='{host}', + username='{username}', + password={repr(password)}, + port={port}, + private_key={repr(private_key)}, + private_key_pass={repr(private_key_pass)}, + cnopts=cnopts + ) as sftp: + sftp.get('{remote_path}', '{local_path}') + print("Successfully downloaded {remote_path} to {local_path}") +except Exception as e: + print(f"SFTP Error: {{str(e)}}", file=sys.stderr) + sys.exit(1) +""" + + try: + result = subprocess.run( + ["python", "-c", py_script], + capture_output=True, + text=True, + check=True + ) + return { + "command_executed": f"pysftp.get({remote_path})", + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": [str(local_file.absolute())] + } + except subprocess.CalledProcessError as e: + return { + "error": "Failed to download file via SFTP", + "stdout": e.stdout, + "stderr": e.stderr + } + +@mcp.tool() +def pysftp_put( + host: str, + username: str, + local_path: str, + remote_path: str, + password: Optional[str] = None, + port: int = 22, + private_key: Optional[str] = None, + private_key_pass: Optional[str] = None, + no_host_key_check: bool = False +) -> Dict[str, Any]: + """ + Upload a file to a remote SFTP server using pysftp. + + Args: + host: Remote hostname or IP address. + username: SSH username. + local_path: Path to the local file to upload. + remote_path: Destination path on the remote server. + password: SSH password (optional if using private key). + port: SSH port (default 22). + private_key: Path to a private key file (optional). + private_key_pass: Password for the private key (optional). + no_host_key_check: If True, bypasses host key verification (insecure). + """ + local_file = Path(local_path) + if not local_file.exists(): + return {"error": f"Local file {local_path} does not exist."} + + py_script = f""" +import pysftp +import sys + +cnopts = pysftp.CnOpts() +if {no_host_key_check}: + cnopts.hostkeys = None + +try: + with pysftp.Connection( + host='{host}', + username='{username}', + password={repr(password)}, + port={port}, + private_key={repr(private_key)}, + private_key_pass={repr(private_key_pass)}, + cnopts=cnopts + ) as sftp: + sftp.put('{local_path}', '{remote_path}') + print("Successfully uploaded {local_path} to {remote_path}") +except Exception as e: + print(f"SFTP Error: {{str(e)}}", file=sys.stderr) + sys.exit(1) +""" + + try: + result = subprocess.run( + ["python", "-c", py_script], + capture_output=True, + text=True, + check=True + ) + return { + "command_executed": f"pysftp.put({local_path})", + "stdout": result.stdout, + "stderr": result.stderr + } + except subprocess.CalledProcessError as e: + return { + "error": "Failed to upload file via SFTP", + "stdout": e.stdout, + "stderr": e.stderr + } + +@mcp.tool() +def pysftp_listdir( + host: str, + username: str, + remote_path: str = ".", + password: Optional[str] = None, + port: int = 22, + private_key: Optional[str] = None, + private_key_pass: Optional[str] = None, + no_host_key_check: bool = False +) -> Dict[str, Any]: + """ + List the contents of a remote directory using pysftp. + + Args: + host: Remote hostname or IP address. + username: SSH username. + remote_path: Remote directory to list (default is home directory "."). + password: SSH password (optional). + port: SSH port (default 22). + private_key: Path to a private key file (optional). + private_key_pass: Password for the private key (optional). + no_host_key_check: If True, bypasses host key verification. + """ + py_script = f""" +import pysftp +import sys + +cnopts = pysftp.CnOpts() +if {no_host_key_check}: + cnopts.hostkeys = None + +try: + with pysftp.Connection( + host='{host}', + username='{username}', + password={repr(password)}, + port={port}, + private_key={repr(private_key)}, + private_key_pass={repr(private_key_pass)}, + cnopts=cnopts + ) as sftp: + files = sftp.listdir('{remote_path}') + for f in files: + print(f) +except Exception as e: + print(f"SFTP Error: {{str(e)}}", file=sys.stderr) + sys.exit(1) +""" + + try: + result = subprocess.run( + ["python", "-c", py_script], + capture_output=True, + text=True, + check=True + ) + return { + "command_executed": f"pysftp.listdir({remote_path})", + "stdout": result.stdout, + "stderr": result.stderr, + "file_list": result.stdout.splitlines() + } + except subprocess.CalledProcessError as e: + return { + "error": "Failed to list directory via SFTP", + "stdout": e.stdout, + "stderr": e.stderr + } + +@mcp.tool() +def pysftp_execute( + host: str, + username: str, + command: str, + password: Optional[str] = None, + port: int = 22, + private_key: Optional[str] = None, + private_key_pass: Optional[str] = None, + no_host_key_check: bool = False +) -> Dict[str, Any]: + """ + Execute a shell command on the remote server using pysftp. + + Args: + host: Remote hostname or IP address. + username: SSH username. + command: The shell command to execute. + password: SSH password (optional). + port: SSH port (default 22). + private_key: Path to a private key file (optional). + private_key_pass: Password for the private key (optional). + no_host_key_check: If True, bypasses host key verification. + """ + py_script = f""" +import pysftp +import sys + +cnopts = pysftp.CnOpts() +if {no_host_key_check}: + cnopts.hostkeys = None + +try: + with pysftp.Connection( + host='{host}', + username='{username}', + password={repr(password)}, + port={port}, + private_key={repr(private_key)}, + private_key_pass={repr(private_key_pass)}, + cnopts=cnopts + ) as sftp: + output = sftp.execute('{command}') + for line in output: + print(line.decode('utf-8'), end='') +except Exception as e: + print(f"SFTP Error: {{str(e)}}", file=sys.stderr) + sys.exit(1) +""" + + try: + result = subprocess.run( + ["python", "-c", py_script], + capture_output=True, + text=True, + check=True + ) + return { + "command_executed": f"pysftp.execute({command})", + "stdout": result.stdout, + "stderr": result.stderr + } + except subprocess.CalledProcessError as e: + return { + "error": "Failed to execute command via SFTP", + "stdout": e.stdout, + "stderr": e.stderr + } + +@mcp.tool() +def pysftp_mkdir( + host: str, + username: str, + remote_directory: str, + password: Optional[str] = None, + port: int = 22, + private_key: Optional[str] = None, + private_key_pass: Optional[str] = None, + no_host_key_check: bool = False +) -> Dict[str, Any]: + """ + Create a directory on the remote SFTP server. + + Args: + host: Remote hostname or IP address. + username: SSH username. + remote_directory: Path of the directory to create. + password: SSH password (optional). + port: SSH port (default 22). + private_key: Path to a private key file (optional). + private_key_pass: Password for the private key (optional). + no_host_key_check: If True, bypasses host key verification. + """ + py_script = f""" +import pysftp +import sys + +cnopts = pysftp.CnOpts() +if {no_host_key_check}: + cnopts.hostkeys = None + +try: + with pysftp.Connection( + host='{host}', + username='{username}', + password={repr(password)}, + port={port}, + private_key={repr(private_key)}, + private_key_pass={repr(private_key_pass)}, + cnopts=cnopts + ) as sftp: + sftp.mkdir('{remote_directory}') + print("Successfully created directory {remote_directory}") +except Exception as e: + print(f"SFTP Error: {{str(e)}}", file=sys.stderr) + sys.exit(1) +""" + + try: + result = subprocess.run( + ["python", "-c", py_script], + capture_output=True, + text=True, + check=True + ) + return { + "command_executed": f"pysftp.mkdir({remote_directory})", + "stdout": result.stdout, + "stderr": result.stderr + } + except subprocess.CalledProcessError as e: + return { + "error": "Failed to create directory via SFTP", + "stdout": e.stdout, + "stderr": e.stderr + } + +@mcp.tool() +def pysftp_remove( + host: str, + username: str, + remote_path: str, + is_directory: bool = False, + password: Optional[str] = None, + port: int = 22, + private_key: Optional[str] = None, + private_key_pass: Optional[str] = None, + no_host_key_check: bool = False +) -> Dict[str, Any]: + """ + Remove a file or directory from the remote SFTP server. + + Args: + host: Remote hostname or IP address. + username: SSH username. + remote_path: Path to the file or directory to remove. + is_directory: Set to True if the path is a directory (uses rmdir). + password: SSH password (optional). + port: SSH port (default 22). + private_key: Path to a private key file (optional). + private_key_pass: Password for the private key (optional). + no_host_key_check: If True, bypasses host key verification. + """ + method = "rmdir" if is_directory else "remove" + py_script = f""" +import pysftp +import sys + +cnopts = pysftp.CnOpts() +if {no_host_key_check}: + cnopts.hostkeys = None + +try: + with pysftp.Connection( + host='{host}', + username='{username}', + password={repr(password)}, + port={port}, + private_key={repr(private_key)}, + private_key_pass={repr(private_key_pass)}, + cnopts=cnopts + ) as sftp: + sftp.{method}('{remote_path}') + print("Successfully removed {remote_path}") +except Exception as e: + print(f"SFTP Error: {{str(e)}}", file=sys.stderr) + sys.exit(1) +""" + + try: + result = subprocess.run( + ["python", "-c", py_script], + capture_output=True, + text=True, + check=True + ) + return { + "command_executed": f"pysftp.{method}({remote_path})", + "stdout": result.stdout, + "stderr": result.stderr + } + except subprocess.CalledProcessError as e: + return { + "error": f"Failed to remove {remote_path} via SFTP", + "stdout": e.stdout, + "stderr": e.stderr + } \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_pysftp/app/pysftp_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_pysftp/app/pysftp_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..7476d142bdee29d9427eb3e0f3cce589f6d52b17 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_pysftp/app/pysftp_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_pysftp/app/pysftp_server.py') +SERVER_NAME = 'biosci_pysftp' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_pysftp/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_pysftp/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_pysftp/app/requirements.txt @@ -0,0 +1 @@ + diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_pysftp/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_pysftp/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..4745e883f4ea3014699cb84b9ce89385933f3da2 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_pysftp/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-pysftp: + build: . + image: mcp-pysftp:latest + container_name: mcp-pysftp + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=pysftp + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_pysftp/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_pysftp/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..70cd7be7831d779f923ea475db19fb00611b8c22 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_pysftp/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - pysftp + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_pysftp/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_pysftp/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_pysftp/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_qualimap/app/qualimap_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_qualimap/app/qualimap_server.py new file mode 100644 index 0000000000000000000000000000000000000000..1bc2dcc383cf5d935daaf8a5396744ebadb1a064 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_qualimap/app/qualimap_server.py @@ -0,0 +1,354 @@ +import subprocess +from pathlib import Path +from typing import Optional, List, Union + +@mcp.tool() +def qualimap_bamqc( + bam_file: str, + out_dir: str, + gff_file: Optional[str] = None, + num_threads: int = 1, + java_mem_size: str = "1200M", + out_format: str = "HTML", + collect_overlap_pairs: bool = False, + hm: int = 3, + nr: int = 1000, + nw: int = 400, + skip_duplicated: bool = False, + paint_chromosome_limits: bool = True, +): + """ + Performs QC analysis of alignment data in BAM format. + + Args: + bam_file: Path to the input BAM file. + out_dir: Path to the output directory. + gff_file: Optional path to a GFF/GTF/BED file to restrict analysis to specific regions. + num_threads: Number of threads to use. + java_mem_size: Memory settings for Java (e.g., '4G', '1200M'). + out_format: Output format (HTML or PDF). + collect_overlap_pairs: If true, overlapping pairs are collected. + hm: Minimum size of homopolymer to be considered. + nr: Number of reads in the chunk to calculate the insert size. + nw: Number of windows to calculate the coverage. + skip_duplicated: If true, duplicated reads are skipped. + paint_chromosome_limits: If true, chromosome limits are painted in the coverage plot. + """ + # Input validation + bam_path = Path(bam_file) + if not bam_path.exists(): + return {"error": f"BAM file not found: {bam_file}"} + + out_path = Path(out_dir) + out_path.mkdir(parents=True, exist_ok=True) + + cmd = [ + "qualimap", "bamqc", + "-bam", str(bam_path), + "-outdir", str(out_path), + "-nt", str(num_threads), + "-outformat", out_format, + "-hm", str(hm), + "-nr", str(nr), + "-nw", str(nw), + "--java-mem-size=" + java_mem_size + ] + + if gff_file: + gff_path = Path(gff_file) + if gff_path.exists(): + cmd.extend(["-gff", str(gff_path)]) + + if collect_overlap_pairs: + cmd.append("-c") + if skip_duplicated: + cmd.append("-sd") + if paint_chromosome_limits: + cmd.append("-p") + + try: + result = subprocess.run(cmd, capture_output=True, text=True, check=True) + return { + "command_executed": " ".join(cmd), + "stdout": result.stdout, + "stderr": result.stderr, + "output_directory": str(out_path.absolute()) + } + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(cmd), + "error": str(e), + "stdout": e.stdout, + "stderr": e.stderr + } + +@mcp.tool() +def qualimap_rnaseq( + bam_file: str, + gtf_file: str, + out_dir: str, + strand_specificity: str = "non-strand-specific", + algorithm: str = "uniquely-mapped-reads", + java_mem_size: str = "1200M", + out_format: str = "HTML", + paired: bool = False, + sorted_bam: bool = False, +): + """ + Performs QC analysis of RNA-seq data. + + Args: + bam_file: Path to the input BAM file. + gtf_file: Path to the genomic annotation file in GTF format. + out_dir: Path to the output directory. + strand_specificity: Strand specificity (non-strand-specific, strand-specific-forward, strand-specific-reverse). + algorithm: Counting algorithm (uniquely-mapped-reads or proportional). + java_mem_size: Memory settings for Java. + out_format: Output format (HTML or PDF). + paired: Set this flag if the library is paired-end. + sorted_bam: Set this flag if the BAM file is already sorted by name. + """ + bam_path = Path(bam_file) + gtf_path = Path(gtf_file) + + if not bam_path.exists(): + return {"error": f"BAM file not found: {bam_file}"} + if not gtf_path.exists(): + return {"error": f"GTF file not found: {gtf_file}"} + + out_path = Path(out_dir) + out_path.mkdir(parents=True, exist_ok=True) + + cmd = [ + "qualimap", "rnaseq", + "-bam", str(bam_path), + "-gtf", str(gtf_path), + "-outdir", str(out_path), + "-p", strand_specificity, + "-a", algorithm, + "-outformat", out_format, + "--java-mem-size=" + java_mem_size + ] + + if paired: + cmd.append("-pe") + if sorted_bam: + cmd.append("-s") + + try: + result = subprocess.run(cmd, capture_output=True, text=True, check=True) + return { + "command_executed": " ".join(cmd), + "stdout": result.stdout, + "stderr": result.stderr, + "output_directory": str(out_path.absolute()) + } + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(cmd), + "error": str(e), + "stdout": e.stdout, + "stderr": e.stderr + } + +@mcp.tool() +def qualimap_multi_bamqc( + data_config: str, + out_dir: str, + label: Optional[str] = None, + java_mem_size: str = "1200M", + out_format: str = "HTML", +): + """ + Performs comparison of multiple BAM files. + + Args: + data_config: Path to the configuration file or a string containing the data description. + out_dir: Path to the output directory. + label: Optional labels for the samples, separated by semicolon. + java_mem_size: Memory settings for Java. + out_format: Output format (HTML or PDF). + """ + config_path = Path(data_config) + if not config_path.exists(): + return {"error": f"Config file not found: {data_config}"} + + out_path = Path(out_dir) + out_path.mkdir(parents=True, exist_ok=True) + + cmd = [ + "qualimap", "multi-bamqc", + "-d", str(config_path), + "-outdir", str(out_path), + "-outformat", out_format, + "--java-mem-size=" + java_mem_size + ] + + if label: + cmd.extend(["-label", label]) + + try: + result = subprocess.run(cmd, capture_output=True, text=True, check=True) + return { + "command_executed": " ".join(cmd), + "stdout": result.stdout, + "stderr": result.stderr, + "output_directory": str(out_path.absolute()) + } + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(cmd), + "error": str(e), + "stdout": e.stdout, + "stderr": e.stderr + } + +@mcp.tool() +def qualimap_counts( + input_file: str, + out_dir: str, + species: str = "human", + java_mem_size: str = "1200M", + out_format: str = "HTML", + count_format: str = "raw", +): + """ + Performs QC analysis of feature counts. + + Args: + input_file: Path to the input counts file. + out_dir: Path to the output directory. + species: Species name (e.g., 'human', 'mouse'). + java_mem_size: Memory settings for Java. + out_format: Output format (HTML or PDF). + count_format: Format of the counts (raw or normalized). + """ + input_path = Path(input_file) + if not input_path.exists(): + return {"error": f"Input file not found: {input_file}"} + + out_path = Path(out_dir) + out_path.mkdir(parents=True, exist_ok=True) + + cmd = [ + "qualimap", "counts", + "-i", str(input_path), + "-outdir", str(out_path), + "-s", species, + "-f", count_format, + "-outformat", out_format, + "--java-mem-size=" + java_mem_size + ] + + try: + result = subprocess.run(cmd, capture_output=True, text=True, check=True) + return { + "command_executed": " ".join(cmd), + "stdout": result.stdout, + "stderr": result.stderr, + "output_directory": str(out_path.absolute()) + } + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(cmd), + "error": str(e), + "stdout": e.stdout, + "stderr": e.stderr + } + +@mcp.tool() +def qualimap_clustering( + input_file: str, + out_dir: str, + java_mem_size: str = "1200M", + num_clusters: int = 2, + out_format: str = "HTML", +): + """ + Performs clustering analysis of samples based on feature counts. + + Args: + input_file: Path to the input counts file. + out_dir: Path to the output directory. + java_mem_size: Memory settings for Java. + num_clusters: Number of clusters to find. + out_format: Output format (HTML or PDF). + """ + input_path = Path(input_file) + if not input_path.exists(): + return {"error": f"Input file not found: {input_file}"} + + out_path = Path(out_dir) + out_path.mkdir(parents=True, exist_ok=True) + + cmd = [ + "qualimap", "clustering", + "-i", str(input_path), + "-outdir", str(out_path), + "-k", str(num_clusters), + "-outformat", out_format, + "--java-mem-size=" + java_mem_size + ] + + try: + result = subprocess.run(cmd, capture_output=True, text=True, check=True) + return { + "command_executed": " ".join(cmd), + "stdout": result.stdout, + "stderr": result.stderr, + "output_directory": str(out_path.absolute()) + } + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(cmd), + "error": str(e), + "stdout": e.stdout, + "stderr": e.stderr + } + +@mcp.tool() +def qualimap_comp_counts( + input_file: str, + out_dir: str, + java_mem_size: str = "1200M", + out_format: str = "HTML", +): + """ + Performs comparison of feature counts between samples. + + Args: + input_file: Path to the input counts file. + out_dir: Path to the output directory. + java_mem_size: Memory settings for Java. + out_format: Output format (HTML or PDF). + """ + input_path = Path(input_file) + if not input_path.exists(): + return {"error": f"Input file not found: {input_file}"} + + out_path = Path(out_dir) + out_path.mkdir(parents=True, exist_ok=True) + + cmd = [ + "qualimap", "comp-counts", + "-i", str(input_path), + "-outdir", str(out_path), + "-outformat", out_format, + "--java-mem-size=" + java_mem_size + ] + + try: + result = subprocess.run(cmd, capture_output=True, text=True, check=True) + return { + "command_executed": " ".join(cmd), + "stdout": result.stdout, + "stderr": result.stderr, + "output_directory": str(out_path.absolute()) + } + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(cmd), + "error": str(e), + "stdout": e.stdout, + "stderr": e.stderr + } \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_qualimap/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_qualimap/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_qualimap/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_r-dwls/app/r-dwls_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_r-dwls/app/r-dwls_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..211728bc0e9433f8029c972178488c98e9cdb0e9 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_r-dwls/app/r-dwls_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_r-dwls/app/r-dwls_server.py') +SERVER_NAME = 'biosci_r_dwls' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_r-dwls/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_r-dwls/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..35bf1ce28d16ccc1ef5ac334ff6372541af71edf --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_r-dwls/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - r-dwls + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_r-restfulr/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_r-restfulr/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..22212f8f2ea88e3e344cdc21fbb245764912ebf0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_r-restfulr/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install r-restfulr via conda (e.g., from bioconda) +RUN conda install -c bioconda r-restfulr -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY app/r-restfulr_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/r-restfulr_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/r-restfulr_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_r-restfulr/app/r-restfulr_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_r-restfulr/app/r-restfulr_server.py new file mode 100644 index 0000000000000000000000000000000000000000..a2d865bff908e628b83491b388ae5bd7a94143ad --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_r-restfulr/app/r-restfulr_server.py @@ -0,0 +1,256 @@ +import subprocess +import json +import tempfile +from pathlib import Path +from typing import Optional, List, Dict, Any + +@mcp.tool() +def restfulr_get( + base_url: str, + path: str = "", + suppress_messages: bool = True +) -> Dict[str, Any]: + """ + Retrieve data from a RESTful service using restfulr, modeling the service as a nested R list. + + Args: + base_url: The base URL of the RESTful service (e.g., 'https://api.github.com'). + path: The resource path within the service (e.g., 'users/octocat/repos'). + suppress_messages: Whether to suppress R startup messages and warnings. + """ + if not base_url.startswith(("http://", "https://")): + return {"error": "base_url must start with http:// or https://"} + + # Construct R script to fetch data and convert to JSON + r_code = f""" + suppressPackageStartupMessages(library(restfulr)) + suppressPackageStartupMessages(library(rjson)) + + container <- RestContainer("{base_url}") + + tryCatch({{ + resource <- if ("{path}" == "") container else container[["{path}"]] + # Convert S4/List objects to JSON for output + cat(toJSON(resource)) + }}, error = function(e) {{ + write(conditionMessage(e), stderr()) + q(status = 1) + }}) + """ + + try: + result = subprocess.run( + ["Rscript", "-e", r_code], + capture_output=True, + text=True, + check=True + ) + + # Attempt to parse the JSON output from R + try: + output_data = json.loads(result.stdout) + except json.JSONDecodeError: + output_data = result.stdout + + return { + "command_executed": f"restfulr::RestContainer('{base_url}')[['{path}']]", + "stdout": result.stdout, + "stderr": result.stderr, + "data": output_data + } + except subprocess.CalledProcessError as e: + return { + "error": "R execution failed", + "command_executed": e.cmd, + "stdout": e.stdout, + "stderr": e.stderr, + "returncode": e.returncode + } + +@mcp.tool() +def restfulr_post( + base_url: str, + path: str, + data_json: str +) -> Dict[str, Any]: + """ + Send data to a RESTful service (POST) using restfulr's list-assignment syntax. + + Args: + base_url: The base URL of the RESTful service. + path: The resource path where data should be posted. + data_json: A JSON string representing the data to be sent. + """ + if not base_url.startswith(("http://", "https://")): + return {"error": "base_url must start with http:// or https://"} + + # Validate JSON + try: + json.loads(data_json) + except json.JSONDecodeError: + return {"error": "data_json must be a valid JSON string"} + + # Construct R script to perform the POST operation + # In restfulr, container[[path]] <- value performs a POST or PUT + r_code = f""" + suppressPackageStartupMessages(library(restfulr)) + suppressPackageStartupMessages(library(rjson)) + + container <- RestContainer("{base_url}") + payload <- fromJSON('{data_json}') + + tryCatch({{ + container[["{path}"]] <- payload + cat("Successfully posted data to {path}") + }}, error = function(e) {{ + write(conditionMessage(e), stderr()) + q(status = 1) + }}) + """ + + try: + result = subprocess.run( + ["Rscript", "-e", r_code], + capture_output=True, + text=True, + check=True + ) + return { + "command_executed": f"restfulr::RestContainer('{base_url}')[['{path}']] <- data", + "stdout": result.stdout, + "stderr": result.stderr + } + except subprocess.CalledProcessError as e: + return { + "error": "R execution failed", + "stdout": e.stdout, + "stderr": e.stderr + } + +@mcp.tool() +def restfulr_delete( + base_url: str, + path: str +) -> Dict[str, Any]: + """ + Delete a resource from a RESTful service using restfulr. + + Args: + base_url: The base URL of the RESTful service. + path: The resource path to be deleted. + """ + if not base_url.startswith(("http://", "https://")): + return {"error": "base_url must start with http:// or https://"} + + # In restfulr, setting a resource to NULL typically triggers a DELETE request + r_code = f""" + suppressPackageStartupMessages(library(restfulr)) + + container <- RestContainer("{base_url}") + + tryCatch({{ + container[["{path}"]] <- NULL + cat("Successfully deleted resource at {path}") + }}, error = function(e) {{ + write(conditionMessage(e), stderr()) + q(status = 1) + }}) + """ + + try: + result = subprocess.run( + ["Rscript", "-e", r_code], + capture_output=True, + text=True, + check=True + ) + return { + "command_executed": f"restfulr::RestContainer('{base_url}')[['{path}']] <- NULL", + "stdout": result.stdout, + "stderr": result.stderr + } + except subprocess.CalledProcessError as e: + return { + "error": "R execution failed", + "stdout": e.stdout, + "stderr": e.stderr + } + +@mcp.tool() +def restfulr_custom_query( + r_script_content: str +) -> Dict[str, Any]: + """ + Execute a custom R script utilizing the restfulr library for complex REST interactions. + + Args: + r_script_content: The full R code to execute. Must include library(restfulr). + """ + with tempfile.NamedTemporaryFile(mode='w', suffix='.R', delete=False) as tmp: + tmp.write(r_script_content) + tmp_path = Path(tmp.name) + + try: + result = subprocess.run( + ["Rscript", str(tmp_path)], + capture_output=True, + text=True, + check=True + ) + return { + "command_executed": f"Rscript {tmp_path.name}", + "stdout": result.stdout, + "stderr": result.stderr + } + except subprocess.CalledProcessError as e: + return { + "error": "Custom R script execution failed", + "stdout": e.stdout, + "stderr": e.stderr + } + finally: + if tmp_path.exists(): + tmp_path.unlink() + +@mcp.tool() +def restfulr_check_status( + base_url: str +) -> Dict[str, Any]: + """ + Check if a RESTful service is reachable and can be modeled by restfulr. + + Args: + base_url: The base URL of the RESTful service. + """ + r_code = f""" + suppressPackageStartupMessages(library(restfulr)) + + tryCatch({{ + container <- RestContainer("{base_url}") + # Attempt to access the root to verify connectivity + print(container) + cat("Status: Reachable") + }}, error = function(e) {{ + write(conditionMessage(e), stderr()) + q(status = 1) + }}) + """ + + try: + result = subprocess.run( + ["Rscript", "-e", r_code], + capture_output=True, + text=True, + check=True + ) + return { + "status": "reachable", + "stdout": result.stdout, + "stderr": result.stderr + } + except subprocess.CalledProcessError as e: + return { + "status": "unreachable", + "error": e.stderr, + "stdout": e.stdout + } \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_r-restfulr/app/r-restfulr_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_r-restfulr/app/r-restfulr_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..8b83bd26323549b2a9b62723f85163241b6b76e9 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_r-restfulr/app/r-restfulr_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_r-restfulr/app/r-restfulr_server.py') +SERVER_NAME = 'biosci_r_restfulr' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_r-restfulr/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_r-restfulr/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_r-restfulr/app/requirements.txt @@ -0,0 +1 @@ + diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_r-restfulr/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_r-restfulr/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..d3cd6a5c426a9c67e6bb91a06fbf4bb16b7a927b --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_r-restfulr/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-r-restfulr: + build: . + image: mcp-r-restfulr:latest + container_name: mcp-r-restfulr + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=r-restfulr + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_r-restfulr/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_r-restfulr/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..7a3fffa6d2a2a0f5bf9295f41130445672e3be64 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_r-restfulr/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - r-restfulr + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_r-restfulr/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_r-restfulr/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_r-restfulr/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_r-seurat-data/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_r-seurat-data/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..4f78b6c6457c79dbfc18097a79a442af86dabdae --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_r-seurat-data/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install r-seurat-data via conda (e.g., from bioconda) +RUN conda install -c bioconda r-seurat-data -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY app/r-seurat-data_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/r-seurat-data_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/r-seurat-data_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_r-seurat-data/app/r-seurat-data_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_r-seurat-data/app/r-seurat-data_server.py new file mode 100644 index 0000000000000000000000000000000000000000..b463667b1a5b2d09ad5e74bd231414982c59f15b --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_r-seurat-data/app/r-seurat-data_server.py @@ -0,0 +1,197 @@ +import subprocess +from typing import Optional +from pathlib import Path + +@mcp.tool() +def seurat_data_available_data() -> dict: + """ + List all available datasets in the SeuratData repository. + This manifest includes metadata such as version, summary, species, system, + number of cells, technology used, and installation status. + """ + # R command to load SeuratData and call AvailableData() + # We use print() to ensure the output is captured in stdout + r_command = "library(SeuratData); print(AvailableData())" + + try: + result = subprocess.run( + ["Rscript", "-e", r_command], + capture_output=True, + text=True, + check=True + ) + return { + "command_executed": f"Rscript -e \"{r_command}\"", + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": [] + } + except subprocess.CalledProcessError as e: + return { + "command_executed": f"Rscript -e \"{r_command}\"", + "stdout": e.stdout, + "stderr": e.stderr, + "error": "Failed to retrieve available datasets. Ensure R and SeuratData are installed." + } + +@mcp.tool() +def seurat_data_install_data( + dataset_name: str +) -> dict: + """ + Install a specific single-cell dataset from the SeuratData repository. + + Args: + dataset_name: The name of the dataset (e.g., 'pbmc3k') or the package name (e.g., 'pbmc3k.SeuratData'). + """ + if not dataset_name: + return {"error": "dataset_name is required"} + + # R command to install the dataset + r_command = f"library(SeuratData); InstallData('{dataset_name}')" + + try: + result = subprocess.run( + ["Rscript", "-e", r_command], + capture_output=True, + text=True, + check=True + ) + return { + "command_executed": f"Rscript -e \"{r_command}\"", + "stdout": result.stdout, + "stderr": result.stderr, + "status": f"Successfully installed {dataset_name}" + } + except subprocess.CalledProcessError as e: + return { + "command_executed": f"Rscript -e \"{r_command}\"", + "stdout": e.stdout, + "stderr": e.stderr, + "error": f"Failed to install dataset '{dataset_name}'. Check if the name is correct and internet connection is available." + } + +@mcp.tool() +def seurat_data_get_summary( + dataset_name: str +) -> dict: + """ + Load a dataset and return its Seurat object summary (features, samples, assays, etc.). + Note: The dataset must be installed first using seurat_data_install_data. + + Args: + dataset_name: The name of the dataset to load (e.g., 'pbmc3k'). + """ + if not dataset_name: + return {"error": "dataset_name is required"} + + # R command to load the dataset and print the object summary + # data() loads it into the environment, then we print the object named after the dataset + r_command = f"library(SeuratData); data('{dataset_name}'); print({dataset_name})" + + try: + result = subprocess.run( + ["Rscript", "-e", r_command], + capture_output=True, + text=True, + check=True + ) + return { + "command_executed": f"Rscript -e \"{r_command}\"", + "stdout": result.stdout, + "stderr": result.stderr, + "dataset": dataset_name + } + except subprocess.CalledProcessError as e: + return { + "command_executed": f"Rscript -e \"{r_command}\"", + "stdout": e.stdout, + "stderr": e.stderr, + "error": f"Failed to load dataset '{dataset_name}'. Ensure it is installed." + } + +@mcp.tool() +def seurat_data_get_citation( + dataset_name: str +) -> dict: + """ + Retrieve the citation information for a specific SeuratData dataset. + + Args: + dataset_name: The name of the dataset. The tool will automatically append '.SeuratData' if not provided. + """ + if not dataset_name: + return {"error": "dataset_name is required"} + + # Ensure the package name format is correct (e.g., pbmc3k.SeuratData) + pkg_name = dataset_name if dataset_name.endswith(".SeuratData") else f"{dataset_name}.SeuratData" + + # R command to get citation + r_command = f"library(SeuratData); print(citation('{pkg_name}'))" + + try: + result = subprocess.run( + ["Rscript", "-e", r_command], + capture_output=True, + text=True, + check=True + ) + return { + "command_executed": f"Rscript -e \"{r_command}\"", + "stdout": result.stdout, + "stderr": result.stderr, + "package": pkg_name + } + except subprocess.CalledProcessError as e: + return { + "command_executed": f"Rscript -e \"{r_command}\"", + "stdout": e.stdout, + "stderr": e.stderr, + "error": f"Failed to get citation for '{pkg_name}'. Ensure the package is installed." + } + +@mcp.tool() +def seurat_data_get_help( + dataset_name: str +) -> dict: + """ + Retrieve the help documentation/metadata for a specific dataset. + This is equivalent to running ?dataset_name in R. + + Args: + dataset_name: The name of the dataset (e.g., 'pbmc3k'). + """ + if not dataset_name: + return {"error": "dataset_name is required"} + + # In R, help() returns a help object. To get text, we use tools::Rd2txt + r_command = ( + f"library(SeuratData); " + f"h <- help('{dataset_name}'); " + f"if(length(h) > 0) {{ " + f" writeLines(as.character(tools::Rd2txt(utils:::.getHelpFile(h)))) " + f"}} else {{ " + f" stop('Help not found') " + f"}}" + ) + + try: + result = subprocess.run( + ["Rscript", "-e", r_command], + capture_output=True, + text=True, + check=True + ) + return { + "command_executed": f"Rscript -e \"{r_command}\"", + "stdout": result.stdout, + "stderr": result.stderr, + "dataset": dataset_name + } + except subprocess.CalledProcessError as e: + return { + "command_executed": f"Rscript -e \"{r_command}\"", + "stdout": e.stdout, + "stderr": e.stderr, + "error": f"Could not find help documentation for '{dataset_name}'." + } \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_r-seurat-data/app/r-seurat-data_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_r-seurat-data/app/r-seurat-data_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..5d7311c6ced448279b5d1add50d3c44bd39e6b6a --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_r-seurat-data/app/r-seurat-data_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_r-seurat-data/app/r-seurat-data_server.py') +SERVER_NAME = 'biosci_r_seurat_data' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_r-seurat-data/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_r-seurat-data/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_r-seurat-data/app/requirements.txt @@ -0,0 +1 @@ + diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_r-seurat-data/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_r-seurat-data/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..357620da42ce443301b7bd731c59bb27487e494a --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_r-seurat-data/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-r-seurat-data: + build: . + image: mcp-r-seurat-data:latest + container_name: mcp-r-seurat-data + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=r-seurat-data + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_r-seurat-data/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_r-seurat-data/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..e9cd6b68d25202014d7c17a31d26c636c51fda26 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_r-seurat-data/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - r-seurat-data + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_r-seurat-data/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_r-seurat-data/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_r-seurat-data/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_rscape/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_rscape/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..6da87503dec651e9bf7aa1604e7e943d70ec971b --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_rscape/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install rscape via conda (e.g., from bioconda) +RUN conda install -c bioconda rscape -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY app/rscape_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/rscape_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/rscape_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_rscape/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_rscape/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_rscape/app/requirements.txt @@ -0,0 +1 @@ + diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_rscape/app/rscape_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_rscape/app/rscape_server.py new file mode 100644 index 0000000000000000000000000000000000000000..d966a05d0a633beb66c383d048db85edd2e3efdf --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_rscape/app/rscape_server.py @@ -0,0 +1,285 @@ +import subprocess +from pathlib import Path +from typing import Optional, Literal, List +import tempfile +import shutil + +@mcp.tool() +def rscape( + input_alignment_file: Path, + operation_mode: Literal[ + "evaluate_conserved_structure", + "predict_new_structure", + "evaluate_given_structure", + "improve_given_structure", + ] = "evaluate_conserved_structure", + e_value: float = 0.01, + output_cov_filename: Optional[str] = None, + output_format: Literal["text", "json", "stockholm"] = "text", + output_prefix: Optional[str] = None, + no_plot: bool = False, + plot_only: bool = False, + verbose: bool = False, + num_cpus: Optional[int] = None, + random_seed: Optional[int] = None, + sequence_type: Literal["rna", "dna"] = "rna", + gap_character: str = "-", + min_covarying_sequences: int = 2, + max_gap_fraction: float = 0.9, + min_sequences: int = 2, + min_alignment_length: int = 2, + max_alignment_length: int = 10000, + min_base_pair_frequency: float = 0.05, + min_posterior_probability: float = 0.5, + max_posterior_probability: float = 0.95, + no_pseudoknots: bool = False, + no_coevolution_analysis: bool = False, + no_phylogenetic_tree: bool = False, + tree_file: Optional[Path] = None, + tree_inference_method: Optional[Literal["fasttree", "raxml"]] = None, + tree_substitution_model: Optional[str] = None, + tree_random_seed: Optional[int] = None, + tree_bootstraps: Optional[int] = None, + tree_output_filename: Optional[str] = None, +) -> dict: + """ + R-scape (RNA Structural Covariation Above Phylogenetic Expectation) looks for evidence + of a conserved RNA secondary structure by measuring pairwise covariations observed + in an input multiple sequence alignment. + + It analyzes all possible pairs, including those in your proposed structure (if you provide one). + R-scape uses a null hypothesis that takes phylogenetic correlations and base composition biases + into account. + + The input is an RNA multiple sequence alignment in Stockholm format, optionally with an + annotated consensus secondary structure. + + Note: 'evaluate_given_structure' and 'improve_given_structure' modes require a consensus + secondary structure annotation in the input Stockholm file. + """ + + # --- Input Validation --- + if not input_alignment_file.is_file(): + raise FileNotFoundError(f"Input alignment file not found: {input_alignment_file}") + + if e_value <= 0: + raise ValueError(f"E-value must be a positive float, got {e_value}") + + if output_cov_filename and not output_cov_filename.strip(): + raise ValueError("Output covariation filename cannot be empty or whitespace.") + if tree_output_filename and not tree_output_filename.strip(): + raise ValueError("Tree output filename cannot be empty or whitespace.") + + if plot_only and no_plot: + raise ValueError("Cannot specify both 'plot_only' and 'no_plot'.") + + if num_cpus is not None and num_cpus <= 0: + raise ValueError(f"Number of CPUs must be a positive integer, got {num_cpus}") + + if min_covarying_sequences <= 0: + raise ValueError(f"Minimum covarying sequences must be a positive integer, got {min_covarying_sequences}") + if min_sequences <= 0: + raise ValueError(f"Minimum sequences must be a positive integer, got {min_sequences}") + if min_alignment_length <= 0: + raise ValueError(f"Minimum alignment length must be a positive integer, got {min_alignment_length}") + if max_alignment_length <= 0: + raise ValueError(f"Maximum alignment length must be a positive integer, got {max_alignment_length}") + if min_alignment_length > max_alignment_length: + raise ValueError("Minimum alignment length cannot be greater than maximum alignment length.") + + if not (0 <= max_gap_fraction <= 1): + raise ValueError(f"Maximum gap fraction must be between 0 and 1, got {max_gap_fraction}") + if not (0 <= min_base_pair_frequency <= 1): + raise ValueError(f"Minimum base pair frequency must be between 0 and 1, got {min_base_pair_frequency}") + if not (0 <= min_posterior_probability <= 1): + raise ValueError(f"Minimum posterior probability must be between 0 and 1, got {min_posterior_probability}") + if not (0 <= max_posterior_probability <= 1): + raise ValueError(f"Maximum posterior probability must be between 0 and 1, got {max_posterior_probability}") + if min_posterior_probability > max_posterior_probability: + raise ValueError("Minimum posterior probability cannot be greater than maximum posterior probability.") + + if tree_file and not tree_file.is_file(): + raise FileNotFoundError(f"Phylogenetic tree file not found: {tree_file}") + + # --- Command Construction --- + cmd = ["rscape"] + + # Create a temporary directory for all outputs + with tempfile.TemporaryDirectory() as temp_dir_str: + temp_output_dir = Path(temp_dir_str) + + # E-value + cmd.extend(["-E", str(e_value)]) + + # Operation Mode + if operation_mode == "evaluate_conserved_structure": + pass # Default behavior + elif operation_mode == "predict_new_structure": + cmd.extend(["--allpairs", "--caco"]) + elif operation_mode == "evaluate_given_structure": + cmd.extend(["--given", "--other"]) + elif operation_mode == "improve_given_structure": + cmd.append("--improve") + + # Output options + if output_cov_filename: + # Use the provided filename for the main covariation output + cmd.extend(["-o", str(temp_output_dir / output_cov_filename)]) + + cmd.extend(["--outfmt", output_format]) + + # Determine the effective prefix for other generated files + # If output_prefix is provided, use it. Otherwise, use input file stem. + effective_prefix = output_prefix if output_prefix else input_alignment_file.stem + + # Use --prefix to direct all other outputs to the temp directory with the chosen prefix + cmd.extend(["--prefix", str(temp_output_dir / effective_prefix)]) + + if no_plot: + cmd.append("--no-plot") + if plot_only: + cmd.append("--plot-only") + if verbose: + cmd.append("--verbose") + + if num_cpus is not None: + cmd.extend(["--cpu", str(num_cpus)]) + if random_seed is not None: + cmd.extend(["--seed", str(random_seed)]) + + if sequence_type == "dna": + cmd.append("--dna") + # else: --rna is default, no flag needed + + if gap_character != "-": + cmd.extend(["--gap-char", gap_character]) + + if min_covarying_sequences != 2: + cmd.extend(["--min-cov", str(min_covarying_sequences)]) + if max_gap_fraction != 0.9: + cmd.extend(["--max-gap", str(max_gap_fraction)]) + if min_sequences != 2: + cmd.extend(["--min-seqs", str(min_sequences)]) + if min_alignment_length != 2: + cmd.extend(["--min-len", str(min_alignment_length)]) + if max_alignment_length != 10000: + cmd.extend(["--max-len", str(max_alignment_length)]) + if min_base_pair_frequency != 0.05: + cmd.extend(["--min-freq", str(min_base_pair_frequency)]) + + # CaCoFold specific options (only relevant if --caco or --improve is used) + if operation_mode in ["predict_new_structure", "improve_given_structure"]: + if min_posterior_probability != 0.5: + cmd.extend(["--min-pp", str(min_posterior_probability)]) + if max_posterior_probability != 0.95: + cmd.extend(["--max-pp", str(max_posterior_probability)]) + if no_pseudoknots: + cmd.append("--no-pseudoknots") + + if no_coevolution_analysis: + cmd.append("--no-coev") + + # Phylogenetic tree options + if no_phylogenetic_tree: + cmd.append("--no-tree") + else: + if tree_file: + cmd.extend(["--tree-file", str(tree_file)]) + if tree_inference_method: + cmd.extend(["--tree-method", tree_inference_method]) + if tree_substitution_model: + cmd.extend(["--tree-model", tree_substitution_model]) + if tree_random_seed is not None: + cmd.extend(["--tree-seed", str(tree_random_seed)]) + if tree_bootstraps is not None: + cmd.extend(["--tree-boot", str(tree_bootstraps)]) + if tree_output_filename: + # Use the provided filename for the tree output + cmd.extend(["--tree-out", str(temp_output_dir / tree_output_filename)]) + + # Positional argument + cmd.append(str(input_alignment_file)) + + # --- Execute Command --- + process = None + try: + process = subprocess.run( + cmd, + capture_output=True, + text=True, + check=True, + cwd=temp_output_dir # Run in the temporary output directory + ) + stdout = process.stdout + stderr = process.stderr + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(cmd), + "stdout": e.stdout, + "stderr": e.stderr, + "error": f"R-scape execution failed with exit code {e.returncode}", + "output_files": [], + } + except FileNotFoundError: + return { + "command_executed": " ".join(cmd), + "stdout": "", + "stderr": "rscape command not found. Please ensure it is installed and in your PATH.", + "error": "rscape executable not found.", + "output_files": [], + } + + # --- Collect Output Files from temp_output_dir --- + generated_output_files: List[Path] = [] + + # Check for the explicitly named covariation file if output_cov_filename was provided + if output_cov_filename: + explicit_cov_file = temp_output_dir / output_cov_filename + if explicit_cov_file.is_file(): + generated_output_files.append(explicit_cov_file) + + # Check for the explicitly named tree file if tree_output_filename was provided + if tree_output_filename: + explicit_tree_file = temp_output_dir / tree_output_filename + if explicit_tree_file.is_file(): + generated_output_files.append(explicit_tree_file) + + # Collect other files based on the effective_prefix + # This includes the default .rscape.cov if -o was not used, and .rscape.ss, .rscape.ps, .rscape.pdf, .rscape.tree + + # Default covariation file (if -o was not used) + if not output_cov_filename: + default_cov_file = temp_output_dir / (effective_prefix + ".rscape.cov") + if default_cov_file.is_file(): + generated_output_files.append(default_cov_file) + + # Predicted secondary structure file + if operation_mode in ["predict_new_structure", "improve_given_structure"]: + ss_file = temp_output_dir / (effective_prefix + ".rscape.ss") + if ss_file.is_file(): + generated_output_files.append(ss_file) + + # Plot files + if not no_plot: # Plots are generated by default unless --no-plot + ps_file = temp_output_dir / (effective_prefix + ".rscape.ps") + if ps_file.is_file(): + generated_output_files.append(ps_file) + pdf_file = temp_output_dir / (effective_prefix + ".rscape.pdf") + if pdf_file.is_file(): + generated_output_files.append(pdf_file) + + # Default tree file (if --tree-out was not used) + if not no_phylogenetic_tree and not tree_output_filename: + default_tree_file = temp_output_dir / (effective_prefix + ".rscape.tree") + if default_tree_file.is_file(): + generated_output_files.append(default_tree_file) + + # Ensure unique paths and convert to string + unique_output_paths = sorted(list(set(str(p) for p in generated_output_files))) + + return { + "command_executed": " ".join(cmd), + "stdout": stdout, + "stderr": stderr, + "output_files": unique_output_paths, + } \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_rscape/app/rscape_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_rscape/app/rscape_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..e90d480663cf0aba37298acf896dbcbf73422812 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_rscape/app/rscape_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_rscape/app/rscape_server.py') +SERVER_NAME = 'biosci_rscape' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_rscape/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_rscape/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..9f99b27eac3a2b1529ad927eb04bfcd496e01e61 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_rscape/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-rscape: + build: . + image: mcp-rscape:latest + container_name: mcp-rscape + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=rscape + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_rscape/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_rscape/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..3c308c44faccab239778870d8f38407f988bf7de --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_rscape/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - rscape + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_rscape/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_rscape/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_rscape/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_rsem/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_rsem/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..9c4583f930b12be15caecb05d882ea48b8f5b7c0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_rsem/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install rsem via conda (e.g., from bioconda) +RUN conda install -c bioconda rsem -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY app/rsem_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/rsem_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/rsem_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_rsem/app/rsem_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_rsem/app/rsem_server.py new file mode 100644 index 0000000000000000000000000000000000000000..cc5c839610186815509a26859f4c525f0e214913 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_rsem/app/rsem_server.py @@ -0,0 +1,622 @@ +import subprocess +from pathlib import Path +from typing import List, Optional, Union +import tempfile +import shutil + +# Define common aligner choices for validation +ALIGNERS = ["bowtie", "bowtie2", "star", "hisat2", "bwa"] + +def _run_command(cmd: List[str], cwd: Optional[Path] = None) -> dict: + """ + Executes a shell command and captures its output. + """ + command_str = " ".join(map(str, cmd)) + try: + process = subprocess.run( + cmd, + cwd=cwd, + check=True, + capture_output=True, + text=True, + encoding="utf-8" + ) + return { + "command_executed": command_str, + "stdout": process.stdout, + "stderr": process.stderr, + "output_files": [], # To be populated by specific tool functions + } + except subprocess.CalledProcessError as e: + return { + "command_executed": command_str, + "stdout": e.stdout, + "stderr": e.stderr, + "error": f"Command failed with exit code {e.returncode}", + "output_files": [], + } + except FileNotFoundError: + return { + "command_executed": command_str, + "stdout": "", + "stderr": "Error: rsem executable not found. Please ensure RSEM is installed and in your PATH.", + "error": "RSEM executable not found.", + "output_files": [], + } + +@mcp.tool() +def rsem_prepare_reference( + reference_fasta: Path, + annotation_gtf: Path, + output_name: str, + aligner: str = "bowtie2", + path_to_aligner: Optional[Path] = None, + num_threads: int = 1, + gff3_genes_as_transcripts: bool = False, + no_polyA: bool = False, + polyA_length: int = 30, + seed: Optional[int] = None, + sort_by_read_name: bool = False, + append_length_and_polyA: bool = False, + remove_polyA: bool = False, + keep_intermediate_files: bool = False, + verbose: bool = False, +) -> dict: + """ + Prepares the reference genome and annotation files for RSEM. + + This function creates the necessary index files for RSEM to quantify gene and isoform + expression. It supports various aligners for indexing. + + Note: Due to the unavailability of direct CLI help, parameters are based on common + RSEM usage and official documentation. + + Args: + reference_fasta: Path to the reference genome FASTA file. + annotation_gtf: Path to the GTF annotation file. + output_name: Base name for the output reference files (e.g., 'human_ref'). + aligner: The aligner to use for indexing. Must be one of 'bowtie', 'bowtie2', 'star', 'hisat2', 'bwa'. + Defaults to 'bowtie2'. + path_to_aligner: Path to the aligner executable if not in PATH. + num_threads: Number of threads to use for parallel processing. Defaults to 1. + gff3_genes_as_transcripts: Treat genes as transcripts when processing GFF3. + (Note: RSEM primarily uses GTF, this option is for GFF3 conversion). + no_polyA: Do not add poly(A) tails to transcripts. + polyA_length: Length of poly(A) tail to add to transcripts. Defaults to 30. + seed: Seed for the random number generator. + sort_by_read_name: Sort the generated BAM file by read name. + append_length_and_polyA: Append length and polyA information to transcript names. + remove_polyA: Remove polyA from transcript names. + keep_intermediate_files: Keep intermediate files generated during reference preparation. + verbose: Output verbose information. + + Returns: + A dictionary containing the command executed, stdout, stderr, and a list of output files. + """ + # Input validation + if not reference_fasta.is_file(): + return { + "command_executed": "", + "stdout": "", + "stderr": f"Error: Reference FASTA file not found: {reference_fasta}", + "error": "Input file not found.", + "output_files": [], + } + if not annotation_gtf.is_file(): + return { + "command_executed": "", + "stdout": "", + "stderr": f"Error: Annotation GTF file not found: {annotation_gtf}", + "error": "Input file not found.", + "output_files": [], + } + if aligner not in ALIGNERS: + return { + "command_executed": "", + "stdout": "", + "stderr": f"Error: Invalid aligner '{aligner}'. Must be one of {', '.join(ALIGNERS)}.", + "error": "Invalid parameter.", + "output_files": [], + } + if num_threads < 1: + return { + "command_executed": "", + "stdout": "", + "stderr": "Error: Number of threads must be at least 1.", + "error": "Invalid parameter.", + "output_files": [], + } + if polyA_length < 0: + return { + "command_executed": "", + "stdout": "", + "stderr": "Error: Poly(A) length cannot be negative.", + "error": "Invalid parameter.", + "output_files": [], + } + + cmd = ["rsem-prepare-reference"] + + # Add aligner option + if aligner == "bowtie": + cmd.append("--bowtie") + elif aligner == "bowtie2": + cmd.append("--bowtie2") + elif aligner == "star": + cmd.append("--star") + elif aligner == "hisat2": + cmd.append("--hisat2") + elif aligner == "bwa": + cmd.append("--bwa") + + if path_to_aligner: + if not path_to_aligner.is_dir(): + return { + "command_executed": "", + "stdout": "", + "stderr": f"Error: Path to aligner directory not found: {path_to_aligner}", + "error": "Invalid parameter.", + "output_files": [], + } + cmd.extend(["--path-to-aligner", str(path_to_aligner)]) + + cmd.extend(["--num-threads", str(num_threads)]) + + if gff3_genes_as_transcripts: + cmd.append("--gff3-genes-as-transcripts") + if no_polyA: + cmd.append("--no-polyA") + else: + cmd.extend(["--polyA-length", str(polyA_length)]) + if seed is not None: + cmd.extend(["--seed", str(seed)]) + if sort_by_read_name: + cmd.append("--sort-by-read-name") + if append_length_and_polyA: + cmd.append("--append-length-and-polyA") + if remove_polyA: + cmd.append("--remove-polyA") + if keep_intermediate_files: + cmd.append("--keep-intermediate-files") + if verbose: + cmd.append("--verbose") + + cmd.extend([str(reference_fasta), str(annotation_gtf), output_name]) + + result = _run_command(cmd) + + # Populate output_files based on expected RSEM reference outputs + output_dir = Path(output_name).parent if Path(output_name).parent.name else Path.cwd() + base_name = Path(output_name).name + + expected_output_files = [ + output_dir / f"{base_name}.idx.fa", + output_dir / f"{base_name}.ti", + output_dir / f"{base_name}.grp", + output_dir / f"{base_name}.seq", + output_dir / f"{base_name}.chrlist", + output_dir / f"{base_name}.transcripts.fa", + ] + # Add aligner index files (example for bowtie2, others would be similar) + if aligner == "bowtie2": + expected_output_files.extend([ + output_dir / f"{base_name}.1.bt2", + output_dir / f"{base_name}.2.bt2", + output_dir / f"{base_name}.3.bt2", + output_dir / f"{base_name}.4.bt2", + output_dir / f"{base_name}.rev.1.bt2", + output_dir / f"{base_name}.rev.2.bt2", + ]) + # Other aligners would have their own index file patterns + + result["output_files"] = [str(f) for f in expected_output_files if f.exists()] + return result + +@mcp.tool() +def rsem_calculate_expression( + read_files: List[Path], + reference_name: str, + sample_name: str, + paired_end: bool = False, + fragment_length_mean: Optional[float] = None, + fragment_length_sd: Optional[float] = None, + fragment_length_distribution: Optional[Path] = None, + no_qualities: bool = False, + strand_specific: bool = False, + forward_prob: float = 0.5, + ci_mem: int = 1024, + ci_bootstraps: int = 0, + seed: Optional[int] = None, + num_threads: int = 1, + aligner: str = "bowtie2", + path_to_aligner: Optional[Path] = None, + output_genome_bam: bool = False, + output_transcript_bam: bool = False, + sort_bam_by_coordinate: bool = False, + sort_bam_by_name: bool = False, + calc_ci: bool = False, + no_bam_output: bool = False, + estimate_rspd: bool = False, + calc_pme: bool = False, + single_cell_prior: bool = False, + tag: Optional[str] = None, + keep_intermediate_files: bool = False, + verbose: bool = False, +) -> dict: + """ + Calculates gene and isoform expression levels from RNA-Seq data using RSEM. + + This function takes raw RNA-Seq reads and an RSEM-prepared reference to quantify + expression. It supports single-end and paired-end reads, various aligners, + and options for credibility interval calculation and BAM output. + + Note: Due to the unavailability of direct CLI help, parameters are based on common + RSEM usage and official documentation. + + Args: + read_files: A list of input read files. For single-end, provide one file. + For paired-end, provide two files (read1, read2). + reference_name: The base name of the RSEM reference generated by `rsem-prepare-reference`. + sample_name: Base name for the output files (e.g., 'my_sample'). + paired_end: Set to True if input reads are paired-end. Defaults to False. + fragment_length_mean: Mean fragment length for paired-end reads. Required if paired_end is True + and fragment_length_distribution is not provided. + fragment_length_sd: Standard deviation of fragment length for paired-end reads. Required if paired_end is True + and fragment_length_distribution is not provided. + fragment_length_distribution: Path to a file containing fragment length distribution. + no_qualities: Set to True if input reads do not have quality scores. Defaults to False. + strand_specific: Set to True if the library is strand-specific. Defaults to False. + forward_prob: Probability of a read coming from the forward strand (0.0 to 1.0). Defaults to 0.5. + ci_mem: Memory in MB for credibility interval calculation. Defaults to 1024. + ci_bootstraps: Number of bootstraps for credibility interval calculation. Defaults to 0 (no CI). + seed: Seed for the random number generator. + num_threads: Number of threads to use for parallel processing. Defaults to 1. + aligner: The aligner to use. Must be one of 'bowtie', 'bowtie2', 'star', 'hisat2', 'bwa'. + Defaults to 'bowtie2'. + path_to_aligner: Path to the aligner executable if not in PATH. + output_genome_bam: Output genome-aligned BAM file. Defaults to False. + output_transcript_bam: Output transcript-aligned BAM file. Defaults to False. + sort_bam_by_coordinate: Sort BAM file by coordinate. Defaults to False. + sort_bam_by_name: Sort BAM file by name. Defaults to False. + calc_ci: Calculate credibility intervals. Defaults to False. + no_bam_output: Do not output BAM files at all. Defaults to False. + estimate_rspd: Estimate read start position distribution. Defaults to False. + calc_pme: Calculate posterior mean estimates. Defaults to False. + single_cell_prior: Use single-cell prior. Defaults to False. + tag: An optional tag to append to output file names. + keep_intermediate_files: Keep intermediate files generated during expression calculation. + verbose: Output verbose information. + + Returns: + A dictionary containing the command executed, stdout, stderr, and a list of output files. + """ + # Input validation + if not read_files: + return { + "command_executed": "", + "stdout": "", + "stderr": "Error: No read files provided.", + "error": "Invalid parameter.", + "output_files": [], + } + for f in read_files: + if not f.is_file(): + return { + "command_executed": "", + "stdout": "", + "stderr": f"Error: Read file not found: {f}", + "error": "Input file not found.", + "output_files": [], + } + if paired_end and len(read_files) != 2: + return { + "command_executed": "", + "stdout": "", + "stderr": "Error: For paired-end reads, exactly two read files must be provided.", + "error": "Invalid parameter.", + "output_files": [], + } + if not paired_end and len(read_files) != 1: + return { + "command_executed": "", + "stdout": "", + "stderr": "Error: For single-end reads, exactly one read file must be provided.", + "error": "Invalid parameter.", + "output_files": [], + } + if aligner not in ALIGNERS: + return { + "command_executed": "", + "stdout": "", + "stderr": f"Error: Invalid aligner '{aligner}'. Must be one of {', '.join(ALIGNERS)}.", + "error": "Invalid parameter.", + "output_files": [], + } + if num_threads < 1: + return { + "command_executed": "", + "stdout": "", + "stderr": "Error: Number of threads must be at least 1.", + "error": "Invalid parameter.", + "output_files": [], + } + if not (0.0 <= forward_prob <= 1.0): + return { + "command_executed": "", + "stdout": "", + "stderr": "Error: Forward probability must be between 0.0 and 1.0.", + "error": "Invalid parameter.", + "output_files": [], + } + if ci_mem < 0: + return { + "command_executed": "", + "stdout": "", + "stderr": "Error: CI memory must be non-negative.", + "error": "Invalid parameter.", + "output_files": [], + } + if ci_bootstraps < 0: + return { + "command_executed": "", + "stdout": "", + "stderr": "Error: CI bootstraps must be non-negative.", + "error": "Invalid parameter.", + "output_files": [], + } + if fragment_length_distribution and not fragment_length_distribution.is_file(): + return { + "command_executed": "", + "stdout": "", + "stderr": f"Error: Fragment length distribution file not found: {fragment_length_distribution}", + "error": "Input file not found.", + "output_files": [], + } + if paired_end and not fragment_length_distribution and (fragment_length_mean is None or fragment_length_sd is None): + return { + "command_executed": "", + "stdout": "", + "stderr": "Error: For paired-end reads, either fragment_length_distribution or both fragment_length_mean and fragment_length_sd must be provided.", + "error": "Invalid parameter.", + "output_files": [], + } + if path_to_aligner: + if not path_to_aligner.is_dir(): + return { + "command_executed": "", + "stdout": "", + "stderr": f"Error: Path to aligner directory not found: {path_to_aligner}", + "error": "Invalid parameter.", + "output_files": [], + } + + cmd = ["rsem-calculate-expression"] + + if paired_end: + cmd.append("--paired-end") + if fragment_length_mean is not None: + cmd.extend(["--fragment-length-mean", str(fragment_length_mean)]) + if fragment_length_sd is not None: + cmd.extend(["--fragment-length-sd", str(fragment_length_sd)]) + if fragment_length_distribution: + cmd.extend(["--fragment-length-distribution", str(fragment_length_distribution)]) + if no_qualities: + cmd.append("--no-qualities") + if strand_specific: + cmd.append("--strand-specific") + cmd.extend(["--forward-prob", str(forward_prob)]) + cmd.extend(["--ci-mem", str(ci_mem)]) + cmd.extend(["--ci-bootstraps", str(ci_bootstraps)]) + if seed is not None: + cmd.extend(["--seed", str(seed)]) + cmd.extend(["--num-threads", str(num_threads)]) + + # Aligner options + cmd.extend([f"--{aligner}"]) + if path_to_aligner: + cmd.extend([f"--{aligner}-path", str(path_to_aligner)]) + + if output_genome_bam: + cmd.append("--output-genome-bam") + if output_transcript_bam: + cmd.append("--output-transcript-bam") + if sort_bam_by_coordinate: + cmd.append("--sort-bam-by-coordinate") + if sort_bam_by_name: + cmd.append("--sort-bam-by-name") + if calc_ci: + cmd.append("--calc-ci") + if no_bam_output: + cmd.append("--no-bam-output") + if estimate_rspd: + cmd.append("--estimate-rspd") + if calc_pme: + cmd.append("--calc-pme") + if single_cell_prior: + cmd.append("--single-cell-prior") + if tag: + cmd.extend(["--tag", tag]) + if keep_intermediate_files: + cmd.append("--keep-intermediate-files") + if verbose: + cmd.append("--verbose") + + cmd.extend([str(f) for f in read_files]) + cmd.extend([reference_name, sample_name]) + + result = _run_command(cmd) + + # Populate output_files + output_dir = Path(sample_name).parent if Path(sample_name).parent.name else Path.cwd() + base_name = Path(sample_name).name + + expected_output_files = [ + output_dir / f"{base_name}.genes.results", + output_dir / f"{base_name}.isoforms.results", + output_dir / f"{base_name}.stat", + ] + + if not no_bam_output: + if output_transcript_bam: + expected_output_files.append(output_dir / f"{base_name}.transcript.bam") + if output_genome_bam: + expected_output_files.append(output_dir / f"{base_name}.genome.bam") + + if calc_ci or ci_bootstraps > 0: + expected_output_files.extend([ + output_dir / f"{base_name}.ci.credibility_intervals.txt", + output_dir / f"{base_name}.ci.genes.results", + output_dir / f"{base_name}.ci.isoforms.results", + ]) + + result["output_files"] = [str(f) for f in expected_output_files if f.exists()] + return result + +@mcp.tool() +def rsem_simulate_reads( + reference_name: str, + model_file: Path, + expression_file: Path, + output_name: str, + num_reads: int, + seed: Optional[int] = None, + paired_end: bool = False, + fragment_length_mean: Optional[float] = None, + fragment_length_sd: Optional[float] = None, + read_length: int = 75, + qual_string: str = "I", + polyA_length: int = 30, + no_qualities: bool = False, + output_fastq: bool = True, + output_sam: bool = False, + output_bam: bool = False, + gzip_output: bool = False, +) -> dict: + """ + Simulates RNA-Seq reads based on RSEM models and expression profiles. + + This function generates synthetic RNA-Seq reads, which can be useful for testing + and benchmarking. It requires an RSEM reference, a model file (e.g., from + `rsem-calculate-expression`), and an expression file. + + Note: Due to the unavailability of direct CLI help, parameters are based on common + RSEM usage and official documentation. + + Args: + reference_name: The base name of the RSEM reference generated by `rsem-prepare-reference`. + model_file: Path to the RSEM model file (e.g., 'sample_name.stat/sample_name.model'). + expression_file: Path to the expression file (e.g., 'sample_name.genes.results' or 'sample_name.isoforms.results'). + output_name: Base name for the simulated output files (e.g., 'simulated_reads'). + num_reads: The total number of reads to simulate. + seed: Seed for the random number generator. + paired_end: Simulate paired-end reads. Defaults to False. + fragment_length_mean: Mean fragment length for paired-end reads. Required if paired_end is True. + fragment_length_sd: Standard deviation of fragment length for paired-end reads. Required if paired_end is True. + read_length: Length of simulated reads. Defaults to 75. + qual_string: Quality string for simulated reads. Defaults to "I". + polyA_length: Length of poly(A) tail to add. Defaults to 30. + no_qualities: Do not output quality scores in the simulated reads. Defaults to False. + output_fastq: Output simulated reads in FASTQ format. Defaults to True. + output_sam: Output simulated reads in SAM format. Defaults to False. + output_bam: Output simulated reads in BAM format. Defaults to False. + gzip_output: Gzip output FASTQ files. Defaults to False. + + Returns: + A dictionary containing the command executed, stdout, stderr, and a list of output files. + """ + # Input validation + if not model_file.is_file(): + return { + "command_executed": "", + "stdout": "", + "stderr": f"Error: Model file not found: {model_file}", + "error": "Input file not found.", + "output_files": [], + } + if not expression_file.is_file(): + return { + "command_executed": "", + "stdout": "", + "stderr": f"Error: Expression file not found: {expression_file}", + "error": "Input file not found.", + "output_files": [], + } + if num_reads <= 0: + return { + "command_executed": "", + "stdout": "", + "stderr": "Error: Number of reads to simulate must be positive.", + "error": "Invalid parameter.", + "output_files": [], + } + if read_length <= 0: + return { + "command_executed": "", + "stdout": "", + "stderr": "Error: Read length must be positive.", + "error": "Invalid parameter.", + "output_files": [], + } + if paired_end and (fragment_length_mean is None or fragment_length_sd is None): + return { + "command_executed": "", + "stdout": "", + "stderr": "Error: For paired-end simulation, both fragment_length_mean and fragment_length_sd must be provided.", + "error": "Invalid parameter.", + "output_files": [], + } + if polyA_length < 0: + return { + "command_executed": "", + "stdout": "", + "stderr": "Error: Poly(A) length cannot be negative.", + "error": "Invalid parameter.", + "output_files": [], + } + + cmd = ["rsem-simulate-reads"] + + if seed is not None: + cmd.extend(["--seed", str(seed)]) + cmd.extend(["--num-reads", str(num_reads)]) + if paired_end: + cmd.append("--paired-end") + if fragment_length_mean is not None: + cmd.extend(["--fragment-length-mean", str(fragment_length_mean)]) + if fragment_length_sd is not None: + cmd.extend(["--fragment-length-sd", str(fragment_length_sd)]) + cmd.extend(["--read-length", str(read_length)]) + cmd.extend(["--qual-string", qual_string]) + cmd.extend(["--polyA-length", str(polyA_length)]) + if no_qualities: + cmd.append("--no-qualities") + if output_fastq: + cmd.append("--output-fastq") + if output_sam: + cmd.append("--output-sam") + if output_bam: + cmd.append("--output-bam") + if gzip_output: + cmd.append("--gzip-output") + + cmd.extend([reference_name, str(model_file), str(expression_file), output_name]) + + result = _run_command(cmd) + + # Populate output_files + output_dir = Path(output_name).parent if Path(output_name).parent.name else Path.cwd() + base_name = Path(output_name).name + + expected_output_files = [] + if output_fastq: + ext = ".fq.gz" if gzip_output else ".fq" + expected_output_files.append(output_dir / f"{base_name}_1{ext}") + if paired_end: + expected_output_files.append(output_dir / f"{base_name}_2{ext}") + if output_sam: + expected_output_files.append(output_dir / f"{base_name}.sim.sam") + if output_bam: + expected_output_files.append(output_dir / f"{base_name}.sim.bam") + + result["output_files"] = [str(f) for f in expected_output_files if f.exists()] + return result \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_rsem/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_rsem/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..0f9b30d0412ddeddd00e0a394d9629671c3c10e2 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_rsem/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - rsem + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_rtg-tools/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_rtg-tools/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_rtg-tools/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_sage-proteomics/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_sage-proteomics/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..8128d701bda20d39d79c4e10af88a48b50b7d62e --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_sage-proteomics/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install sage-proteomics via conda (e.g., from bioconda) +RUN conda install -c bioconda sage-proteomics -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY sage-proteomics_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/sage-proteomics_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/sage-proteomics_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_sage-proteomics/app/sage-proteomics_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_sage-proteomics/app/sage-proteomics_server.py new file mode 100644 index 0000000000000000000000000000000000000000..543cc2df78dce4329ca17d462218e56d48501159 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_sage-proteomics/app/sage-proteomics_server.py @@ -0,0 +1,161 @@ +import json +import subprocess +import tempfile +from pathlib import Path +from typing import Dict, List, Literal, Optional + +# This is a placeholder for the MCP decorator. +# In a real MCP environment, you would import it, e.g., `from mcp import tool`. +class mcp: + def tool(func): + return func + +@mcp.tool +def sage_search( + mzml_paths: List[Path], + fasta: Path, + output_directory: Optional[Path] = None, + peptide_fdr: float = 0.01, + protein_fdr: float = 0.01, + report_psms: int = 1, + chimera: bool = False, + min_ion_index: int = 2, + db_bucket: Optional[str] = None, + missed_cleavages: int = 0, + fragment_min_mz: float = 200.0, + fragment_max_mz: float = 2000.0, + peptide_min_len: int = 5, + peptide_max_len: int = 50, + peptide_min_mass: float = 500.0, + peptide_max_mass: float = 5000.0, + static_mods: Optional[Dict[str, float]] = None, + variable_mods: Optional[Dict[str, float]] = None, + max_variable_mods: int = 2, + enzyme_cleave_at: str = "KR", + enzyme_restrict: Optional[str] = "P", + enzyme_c_terminal: bool = True, + decoy_tag: str = "rev_", + generate_decoys: bool = True, + quant_tmt: Optional[Literal["Tmt10", "Tmt11", "Tmt16", "Tmt18"]] = None, + quant_tmt_level: int = 3, + quant_lfq: bool = False, + quant_lfq_min_peptide_ratios: int = 1, + quant_lfq_min_peptides: int = 1, +) -> Dict: + """ + Runs a Sage proteomics search, dynamically generating the required JSON configuration. + + This tool encapsulates the functionality of the `sage` command-line tool, + exposing all its configuration options as parameters. It handles the creation + of the JSON parameter file, execution of the search, and management of output files. + """ + # 1. Input Validation + if not fasta.is_file(): + raise FileNotFoundError(f"FASTA file not found: {fasta}") + if not mzml_paths: + raise ValueError("At least one mzML file must be provided in `mzml_paths`.") + for path in mzml_paths: + if not Path(path).is_file(): + raise FileNotFoundError(f"mzML file not found: {path}") + + if not 0.0 <= peptide_fdr <= 1.0: + raise ValueError("`peptide_fdr` must be between 0.0 and 1.0.") + if not 0.0 <= protein_fdr <= 1.0: + raise ValueError("`protein_fdr` must be between 0.0 and 1.0.") + if quant_tmt and quant_tmt_level not in [2, 3]: + raise ValueError("`quant_tmt_level` must be 2 or 3.") + + # 2. Setup temporary directory for configuration and potential output + with tempfile.TemporaryDirectory() as temp_dir_str: + temp_dir = Path(temp_dir_str) + config_path = temp_dir / "params.json" + + # Determine output directory + if output_directory: + # Use user-provided directory + output_path = Path(output_directory) + output_path.mkdir(parents=True, exist_ok=True) + else: + # Create a temporary directory for results if none is specified + output_path = temp_dir / "sage_results" + output_path.mkdir() + + # 3. Construct the configuration dictionary + config = { + "database": { + "bucket": db_bucket, + "fasta": str(fasta), + "missed_cleavages": missed_cleavages, + "fragment_min_mz": fragment_min_mz, + "fragment_max_mz": fragment_max_mz, + "peptide_min_len": peptide_min_len, + "peptide_max_len": peptide_max_len, + "peptide_min_mass": peptide_min_mass, + "peptide_max_mass": peptide_max_mass, + "min_ion_index": min_ion_index, + "static_mods": static_mods or {}, + "variable_mods": variable_mods or {}, + "max_variable_mods": max_variable_mods, + "enzyme": { + "cleave_at": enzyme_cleave_at, + "restrict": enzyme_restrict, + "c_terminal": enzyme_c_terminal, + }, + "decoy_tag": decoy_tag, + "generate_decoys": generate_decoys, + }, + "quant": { + "tmt": quant_tmt, + "tmt_level": quant_tmt_level, + "lfq": quant_lfq, + "lfq_min_peptide_ratios": quant_lfq_min_peptide_ratios, + "lfq_min_peptides": quant_lfq_min_peptides, + }, + "output_directory": str(output_path), + "peptide_fdr": peptide_fdr, + "protein_fdr": protein_fdr, + "chimera": chimera, + "report_psms": report_psms, + "mzml_paths": [str(p) for p in mzml_paths], + } + + # 4. Write the JSON configuration file + with open(config_path, "w") as f: + json.dump(config, f, indent=4) + + # 5. Build and execute the command + cmd = ["sage", str(config_path), "--output-directory", str(output_path)] + command_executed = " ".join(cmd) + + try: + result = subprocess.run( + cmd, + capture_output=True, + text=True, + check=True, + ) + except FileNotFoundError: + raise RuntimeError( + "The 'sage' executable was not found. " + "Please ensure sage-proteomics is installed and in your system's PATH." + ) + except subprocess.CalledProcessError as e: + error_message = ( + f"Sage execution failed with exit code {e.returncode}.\n" + f"Command: {command_executed}\n" + f"Stdout: {e.stdout}\n" + f"Stderr: {e.stderr}" + ) + raise RuntimeError(error_message) from e + + # 6. Collect output files + output_files = sorted([str(f) for f in output_path.glob("*")]) + + # 7. Return structured output + return { + "command_executed": command_executed, + "stdout": result.stdout, + "stderr": result.stderr, + "output_directory": str(output_path), + "output_files": output_files, + } \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_sage-proteomics/app/sage-proteomics_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_sage-proteomics/app/sage-proteomics_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..4f1ac14f19c34c8ed48cc7d9819720e4bc253b08 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_sage-proteomics/app/sage-proteomics_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_sage-proteomics/app/sage-proteomics_server.py') +SERVER_NAME = 'biosci_sage_proteomics' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_sage-proteomics/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_sage-proteomics/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..6cc0fa47608daa6e5df128751fd6f12c67328714 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_sage-proteomics/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-sage-proteomics: + build: . + image: mcp-sage-proteomics:latest + container_name: mcp-sage-proteomics + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=sage-proteomics + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_sage-proteomics/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_sage-proteomics/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..cdbcb6dde46e0490609022353633074ec531fc92 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_sage-proteomics/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - sage-proteomics + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_sage-proteomics/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_sage-proteomics/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_sage-proteomics/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_samblaster/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_samblaster/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..0154c12e9ad7fdc54eed5ca15cd0e70a6e64604f --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_samblaster/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install samblaster via conda (e.g., from bioconda) +RUN conda install -c bioconda samblaster -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY app/samblaster_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/samblaster_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/samblaster_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_samblaster/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_samblaster/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_samblaster/app/requirements.txt @@ -0,0 +1 @@ + diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_samblaster/app/samblaster_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_samblaster/app/samblaster_server.py new file mode 100644 index 0000000000000000000000000000000000000000..ec80c4e428c6919798383b3026f4c9643770eadf --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_samblaster/app/samblaster_server.py @@ -0,0 +1,192 @@ +import subprocess +from pathlib import Path +from typing import Optional, List, Dict, Any + +# No need to import mcp as per instructions + +@mcp.tool() +def samblaster( + input_sam_file: Optional[Path] = None, + output_sam_file: Optional[Path] = None, + discordant_file: Optional[Path] = None, + splitter_file: Optional[Path] = None, + unmapped_file: Optional[Path] = None, + accept_dup_marks: bool = False, + exclude_dups: bool = False, + remove_dups: bool = False, + add_mate_tags: bool = False, + ignore_unmated: bool = False, + compatibility_mode_M: bool = False, + max_read_length: int = 500, + max_split_count: int = 2, + max_unmapped_bases: int = 50, + min_indel_size: int = 50, + min_non_overlap: int = 20, + min_clip_size: int = 20, + quiet: bool = False, +) -> Dict[str, Any]: + """ + Marks duplicates in and extracts discordant and split reads from SAM files. + + Input SAM file must contain sequence header and be grouped by read IDs (QNAME). + Input typically contains paired-end data, although singleton data is allowed + with --ignoreUnmated option. + Output will be all alignments in the same order as input, with duplicates + marked with FLAG 0x400, unless --removeDups is used. + + Args: + input_sam_file: Path to the input SAM file. If None, reads from stdin. + output_sam_file: Path to the output SAM file for all input alignments. + If None, writes to stdout. + discordant_file: Path to output discordant read pairs to this file. + [no discordant file output by default] + splitter_file: Path to output split reads to this file. + [no splitter file output by default] + unmapped_file: Path to output unmapped/clipped reads as FASTQ/FASTA to this file. + Requires soft clipping in input file. Outputs FASTQ if QUAL info + available, otherwise FASTA. [no unmapped file output by default] + accept_dup_marks: Accept duplicate marks already in input file instead of + looking for duplicates in the input. + exclude_dups: Exclude reads marked as duplicates from discordant, splitter, + and/or unmapped files. + remove_dups: Remove duplicates reads from all output files. (Implies --excludeDups). + add_mate_tags: Add MC and MQ tags to all output paired-end SAM lines. + ignore_unmated: Suppress abort on unmated alignments. Use only when sure input + is read-id grouped, and either paired-end alignments have been + filtered or the input file contains singleton reads. + Not recommended for general use on paired-end data as it disables + checks that detect incorrectly sorted input. + compatibility_mode_M: Run in compatibility mode; both FLAG 0x100 and 0x800 are + considered supplementary (chimeric). Similar to BWA MEM -M option. + max_read_length: Maximum allowed length of the SEQ/QUAL string in the input file. + Primarily useful for marking duplicates in files containing + singleton long reads. Default: 500. + max_split_count: Maximum number of split alignments for a read to be included + in the splitter file. Default: 2. + max_unmapped_bases: Maximum number of un-aligned bases between two alignments + to be included in the splitter file. Default: 50. + min_indel_size: Minimum structural variant feature size for split alignments + to be included in the splitter file. Default: 50. + min_non_overlap: Minimum non-overlapping base pairs between two alignments + for a read to be included in the splitter file. Default: 20. + min_clip_size: Minimum number of bases a mapped read must be clipped to be + included in the unmapped file. Default: 20. + quiet: Output fewer statistics. + + Returns: + A dictionary containing the command executed, stdout, stderr, and a list of + generated output files. + """ + command = ["samblaster"] + output_files = [] + + # Input validation and command construction for file paths + if input_sam_file: + if not input_sam_file.is_file(): + raise FileNotFoundError(f"Input SAM file not found: {input_sam_file}") + command.extend(["-i", str(input_sam_file)]) + + if output_sam_file: + output_sam_file.parent.mkdir(parents=True, exist_ok=True) + command.extend(["-o", str(output_sam_file)]) + output_files.append(str(output_sam_file)) + + if discordant_file: + discordant_file.parent.mkdir(parents=True, exist_ok=True) + command.extend(["-d", str(discordant_file)]) + output_files.append(str(discordant_file)) + + if splitter_file: + splitter_file.parent.mkdir(parents=True, exist_ok=True) + command.extend(["-s", str(splitter_file)]) + output_files.append(str(splitter_file)) + + if unmapped_file: + unmapped_file.parent.mkdir(parents=True, exist_ok=True) + command.extend(["-u", str(unmapped_file)]) + output_files.append(str(unmapped_file)) + + # Other Options (boolean flags) + if accept_dup_marks: + command.append("--acceptDupMarks") + if exclude_dups: + command.append("--excludeDups") + if remove_dups: + command.append("--removeDups") + if add_mate_tags: + command.append("--addMateTags") + if ignore_unmated: + command.append("--ignoreUnmated") + if compatibility_mode_M: + command.append("-M") + + # Integer parameters with validation and command construction + if not max_read_length >= 0: + raise ValueError("max_read_length must be a non-negative integer.") + command.extend(["--maxReadLength", str(max_read_length)]) + + if not max_split_count >= 0: + raise ValueError("max_split_count must be a non-negative integer.") + command.extend(["--maxSplitCount", str(max_split_count)]) + + if not max_unmapped_bases >= 0: + raise ValueError("max_unmapped_bases must be a non-negative integer.") + command.extend(["--maxUnmappedBases", str(max_unmapped_bases)]) + + if not min_indel_size >= 0: + raise ValueError("min_indel_size must be a non-negative integer.") + command.extend(["--minIndelSize", str(min_indel_size)]) + + if not min_non_overlap >= 0: + raise ValueError("min_non_overlap must be a non-negative integer.") + command.extend(["--minNonOverlap", str(min_non_overlap)]) + + if not min_clip_size >= 0: + raise ValueError("min_clip_size must be a non-negative integer.") + command.extend(["--minClipSize", str(min_clip_size)]) + + if quiet: + command.append("-q") + + process = None + try: + process = subprocess.run( + command, + check=True, + capture_output=True, + text=True, + ) + stdout = process.stdout + stderr = process.stderr + except FileNotFoundError: + return { + "command_executed": " ".join(command), + "stdout": "", + "stderr": "Error: samblaster command not found. Is it installed and in PATH?", + "output_files": [], + "error": "samblaster_not_found", + } + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(e.cmd), + "stdout": e.stdout, + "stderr": e.stderr, + "output_files": [], + "error": "samblaster_execution_failed", + "returncode": e.returncode, + } + except Exception as e: + return { + "command_executed": " ".join(command), + "stdout": "", + "stderr": f"An unexpected error occurred: {e}", + "output_files": [], + "error": "unexpected_error", + } + + return { + "command_executed": " ".join(command), + "stdout": stdout, + "stderr": stderr, + "output_files": output_files, + } \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_samblaster/app/samblaster_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_samblaster/app/samblaster_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..3525dda40753eec78c7d880367a53258fd0439ce --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_samblaster/app/samblaster_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_samblaster/app/samblaster_server.py') +SERVER_NAME = 'biosci_samblaster' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_samblaster/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_samblaster/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..1c0aad615e7e387bc8d8a3e8bc93b97ccb4db522 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_samblaster/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-samblaster: + build: . + image: mcp-samblaster:latest + container_name: mcp-samblaster + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=samblaster + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_samblaster/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_samblaster/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..af6fcea7f7d71b65f0b4f53e04af701e5e315593 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_samblaster/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - samblaster + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_samblaster/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_samblaster/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_samblaster/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_scepia/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_scepia/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..04282a68ceb55d2c1849929c9af01797a2d148ed --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_scepia/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install scepia via conda (e.g., from bioconda) +RUN conda install -c bioconda scepia -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY app/scepia_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/scepia_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/scepia_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_scepia/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_scepia/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_scepia/app/requirements.txt @@ -0,0 +1 @@ + diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_scepia/app/scepia_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_scepia/app/scepia_server.py new file mode 100644 index 0000000000000000000000000000000000000000..dc405a9420deae0df6875b4c5e628e544c310727 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_scepia/app/scepia_server.py @@ -0,0 +1,160 @@ +import subprocess +import logging +from pathlib import Path +from typing import Optional, List, Dict, Any + +# This is a placeholder for the MCP framework's decorator. +# In a real MCP environment, this would be provided by the framework. +class McpToolDecorator: + def __init__(self): + pass + def __call__(self, func): + return func +mcp = type('mcp', (), {'tool': McpToolDecorator()})() + + +@mcp.tool() +def infer_motifs( + input_file: Path, + output_dir: Path, +) -> Dict[str, Any]: + """ + Infers transcription factor motif activity from single cell RNA-seq data. + + This command works on any file that is supported by scanpy.read(). + It is recommended to process your data, including QC, filtering, normalization, + and clustering, using scanpy and saving the results to an .h5ad file. + + Args: + input_file: Path to the input single-cell data file (e.g., .h5ad, .csv). + output_dir: Path to the directory where output files will be saved. + + Returns: + A dictionary containing the command executed, stdout, stderr, and a list + of output files generated. + """ + # Input validation + if not input_file.is_file(): + raise FileNotFoundError(f"Input file not found: {input_file}") + + try: + output_dir.mkdir(parents=True, exist_ok=True) + except OSError as e: + raise OSError(f"Could not create output directory {output_dir}: {e}") + + command = [ + "scepia", + "infer_motifs", + str(input_file), + str(output_dir), + ] + + command_executed = " ".join(command) + logging.info(f"Executing command: {command_executed}") + + try: + result = subprocess.run( + command, + check=True, + capture_output=True, + text=True, + encoding='utf-8' + ) + + # List all files in the output directory as they are not explicitly named + output_files = [str(p) for p in output_dir.rglob("*") if p.is_file()] + + return { + "command_executed": command_executed, + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": output_files, + } + except FileNotFoundError: + raise RuntimeError("scepia command not found. Please ensure it is installed and in your PATH.") + except subprocess.CalledProcessError as e: + logging.error(f"Command failed with exit code {e.returncode}") + logging.error(f"stdout: {e.stdout}") + logging.error(f"stderr: {e.stderr}") + raise RuntimeError( + f"scepia infer_motifs failed with exit code {e.returncode}.\n" + f"Stderr: {e.stderr}\n" + f"Stdout: {e.stdout}" + ) + +@mcp.tool() +def area27( + bamfile: Path, + outfile: Path, + threads: Optional[int] = None, +) -> Dict[str, Any]: + """ + Calculates the enhancer-based regulatory potential (ERP) score per gene. + + This approach is based on the one developed by Wang et al., 2016, but the + score is calculated based only on H3K27ac signal in enhancers. It uses + log-transformed, z-score normalized H3K27ac read counts in 2kb windows + centered at enhancer locations. + + Args: + bamfile: Path to the input H3K27ac BAM file (mapped to hg38). + outfile: Path for the output file. + threads: Number of threads to use (-N). + + Returns: + A dictionary containing the command executed, stdout, stderr, and the + path to the output file. + """ + # Input validation + if not bamfile.is_file(): + raise FileNotFoundError(f"Input BAM file not found: {bamfile}") + if threads is not None and threads <= 0: + raise ValueError("Number of threads must be a positive integer.") + + # Ensure output directory exists + try: + outfile.parent.mkdir(parents=True, exist_ok=True) + except OSError as e: + raise OSError(f"Could not create output directory {outfile.parent}: {e}") + + command = [ + "scepia", + "area27", + str(bamfile), + str(outfile), + ] + + if threads is not None: + command.extend(["-N", str(threads)]) + + command_executed = " ".join(command) + logging.info(f"Executing command: {command_executed}") + + try: + result = subprocess.run( + command, + check=True, + capture_output=True, + text=True, + encoding='utf-8' + ) + + output_files = [str(outfile)] if outfile.exists() else [] + + return { + "command_executed": command_executed, + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": output_files, + } + except FileNotFoundError: + raise RuntimeError("scepia command not found. Please ensure it is installed and in your PATH.") + except subprocess.CalledProcessError as e: + logging.error(f"Command failed with exit code {e.returncode}") + logging.error(f"stdout: {e.stdout}") + logging.error(f"stderr: {e.stderr}") + raise RuntimeError( + f"scepia area27 failed with exit code {e.returncode}.\n" + f"Stderr: {e.stderr}\n" + f"Stdout: {e.stdout}" + ) \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_scepia/app/scepia_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_scepia/app/scepia_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..650ca87847f15a4def550866dd39702e05af40bd --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_scepia/app/scepia_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_scepia/app/scepia_server.py') +SERVER_NAME = 'biosci_scepia' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_scepia/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_scepia/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..ff1fe0f2b6b959ce5fa7636b0b807433c05cf569 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_scepia/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-scepia: + build: . + image: mcp-scepia:latest + container_name: mcp-scepia + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=scepia + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_scepia/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_scepia/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..9dcb571de2b291ca8265fc1b2b85b8817fd69792 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_scepia/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - scepia + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_scepia/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_scepia/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_scepia/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_scrnasim-toolz/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_scrnasim-toolz/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..b1631ddaeba5f2ee2e908c157d1d56c51355d789 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_scrnasim-toolz/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install scrnasim-toolz via conda (e.g., from bioconda) +RUN conda install -c bioconda scrnasim-toolz -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY app/scrnasim-toolz_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/scrnasim-toolz_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/scrnasim-toolz_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_scrnasim-toolz/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_scrnasim-toolz/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_scrnasim-toolz/app/requirements.txt @@ -0,0 +1 @@ + diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_scrnasim-toolz/app/scrnasim-toolz_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_scrnasim-toolz/app/scrnasim-toolz_server.py new file mode 100644 index 0000000000000000000000000000000000000000..0ecb606fbd6674278a8295dd79314e430170f9a0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_scrnasim-toolz/app/scrnasim-toolz_server.py @@ -0,0 +1,481 @@ +import subprocess +import tempfile +from pathlib import Path +from typing import Dict, List, Optional, Union + +# MCP decorator placeholder +class mcp: + @staticmethod + def tool(func): + return func + +@mcp.tool +def transcript_sampler( + gene_counts: Path, + total_transcripts: int, + num_cells: int, + output_counts: Path, + seed: Optional[int] = None, +) -> Dict[str, Union[str, List[str]]]: + """ + Samples the number of transcripts for each gene in each cell. + + This tool simulates the stochastic nature of gene expression. Given a file with + average expression levels for genes, it generates a count for each gene in each + simulated cell using a Poisson distribution. + + Args: + gene_counts: Path to a CSV file with columns "GeneID" and "Counts" specifying + the average number of transcripts per gene. + total_transcripts: The total number of transcripts to be present in a single cell. + num_cells: The number of cells to simulate. + output_counts: Path to the output CSV file where the sampled counts will be written. + seed: A random seed for reproducibility. + + Returns: + A dictionary containing the executed command, stdout, stderr, and a list of output files. + """ + # Input validation + if not gene_counts.is_file(): + raise FileNotFoundError(f"Input file not found: {gene_counts}") + if total_transcripts <= 0: + raise ValueError("total_transcripts must be a positive integer.") + if num_cells <= 0: + raise ValueError("num_cells must be a positive integer.") + + cmd = [ + "transcript-sampler", + "--gene-counts", str(gene_counts), + "--total-transcripts", str(total_transcripts), + "--num-cells", str(num_cells), + "--output-counts", str(output_counts), + ] + if seed is not None: + cmd.extend(["--seed", str(seed)]) + + try: + result = subprocess.run( + cmd, + capture_output=True, + text=True, + check=True, + ) + return { + "command_executed": " ".join(cmd), + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": [str(output_counts)], + } + except FileNotFoundError: + raise RuntimeError("transcript-sampler executable not found. Is scrnasim-toolz installed and in your PATH?") + except subprocess.CalledProcessError as e: + raise RuntimeError( + f"transcript-sampler failed with exit code {e.returncode}:\n" + f"STDOUT: {e.stdout}\n" + f"STDERR: {e.stderr}" + ) + +@mcp.tool +def structure_generator( + sampled_counts: Path, + annotation_gtf: Path, + intron_inclusion_prob: float, + output_gtf: Path, + output_counts: Path, + seed: Optional[int] = None, +) -> Dict[str, Union[str, List[str]]]: + """ + Generates exon/intron structures for each transcript, simulating incomplete splicing. + + For each transcript sampled, this tool decides whether to include its introns based on a + given probability. It produces a new GTF file with the generated structures and a + corresponding counts file. + + Args: + sampled_counts: Path to the CSV file with sampled transcript counts from transcript_sampler. + annotation_gtf: Path to the reference GTF/GFF file with transcript annotations. + intron_inclusion_prob: The probability of an intron being included in a transcript. + output_gtf: Path to the output GTF file with the generated transcript structures. + output_counts: Path to the output file saving the number of transcripts for each unique structure. + seed: A random seed for reproducibility. + + Returns: + A dictionary containing the executed command, stdout, stderr, and a list of output files. + """ + # Input validation + if not sampled_counts.is_file(): + raise FileNotFoundError(f"Input file not found: {sampled_counts}") + if not annotation_gtf.is_file(): + raise FileNotFoundError(f"Annotation file not found: {annotation_gtf}") + if not (0.0 <= intron_inclusion_prob <= 1.0): + raise ValueError("intron_inclusion_prob must be between 0.0 and 1.0.") + + cmd = [ + "structure-generator", + "--sampled-counts", str(sampled_counts), + "--annotation-gtf", str(annotation_gtf), + "--intron-inclusion-prob", str(intron_inclusion_prob), + "--output-gtf", str(output_gtf), + "--output-counts", str(output_counts), + ] + if seed is not None: + cmd.extend(["--seed", str(seed)]) + + try: + result = subprocess.run( + cmd, + capture_output=True, + text=True, + check=True, + ) + return { + "command_executed": " ".join(cmd), + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": [str(output_gtf), str(output_counts)], + } + except FileNotFoundError: + raise RuntimeError("structure-generator executable not found. Is scrnasim-toolz installed and in your PATH?") + except subprocess.CalledProcessError as e: + raise RuntimeError( + f"structure-generator failed with exit code {e.returncode}:\n" + f"STDOUT: {e.stdout}\n" + f"STDERR: {e.stderr}" + ) + +@mcp.tool +def sequence_extractor( + gtf: Path, + genome_fasta: Path, + output_fasta: Path, + add_polya: bool = False, + polya_len_mean: Optional[int] = None, + polya_len_std: Optional[int] = None, + polya_non_a_freq: Optional[float] = None, +) -> Dict[str, Union[str, List[str]]]: + """ + Extracts transcript sequences based on a GTF file and a reference genome. + + This tool constructs transcript sequences from the structures defined in the input GTF file. + It can optionally add poly(A) tails to the extracted sequences. + + Args: + gtf: Path to the GTF file with transcript structures (e.g., from structure_generator). + genome_fasta: Path to the reference genome FASTA file. + output_fasta: Path to the output FASTA file for the extracted transcript sequences. + add_polya: If True, add poly(A) tails to the transcripts. + polya_len_mean: Mean length of the poly(A) tail. Required if add_polya is True. + polya_len_std: Standard deviation of the poly(A) tail length. Required if add_polya is True. + polya_non_a_freq: Frequency of non-A nucleotides in the poly(A) tail. Required if add_polya is True. + + Returns: + A dictionary containing the executed command, stdout, stderr, and a list of output files. + """ + # Input validation + if not gtf.is_file(): + raise FileNotFoundError(f"GTF file not found: {gtf}") + if not genome_fasta.is_file(): + raise FileNotFoundError(f"Genome FASTA file not found: {genome_fasta}") + + cmd = [ + "sequence-extractor", + "--gtf", str(gtf), + "--genome-fasta", str(genome_fasta), + "--output-fasta", str(output_fasta), + ] + + if add_polya: + if polya_len_mean is None or polya_len_std is None or polya_non_a_freq is None: + raise ValueError("If add_polya is True, polya_len_mean, polya_len_std, and polya_non_a_freq must be provided.") + if polya_len_mean <= 0: + raise ValueError("polya_len_mean must be positive.") + if polya_len_std < 0: + raise ValueError("polya_len_std must be non-negative.") + if not (0.0 <= polya_non_a_freq <= 1.0): + raise ValueError("polya_non_a_freq must be between 0.0 and 1.0.") + + cmd.append("--add-polya") + cmd.extend(["--polya-len-mean", str(polya_len_mean)]) + cmd.extend(["--polya-len-std", str(polya_len_std)]) + cmd.extend(["--polya-non-a-freq", str(polya_non_a_freq)]) + + try: + result = subprocess.run( + cmd, + capture_output=True, + text=True, + check=True, + ) + return { + "command_executed": " ".join(cmd), + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": [str(output_fasta)], + } + except FileNotFoundError: + raise RuntimeError("sequence-extractor executable not found. Is scrnasim-toolz installed and in your PATH?") + except subprocess.CalledProcessError as e: + raise RuntimeError( + f"sequence-extractor failed with exit code {e.returncode}:\n" + f"STDOUT: {e.stdout}\n" + f"STDERR: {e.stderr}" + ) + +@mcp.tool +def priming_site_predictor( + transcript_fasta: Path, + output_sites: Path, + primer_seq: str = "TTTTTTTTTTTTTTT", + min_consecutive_a: int = 6, + max_mismatches: int = 1, +) -> Dict[str, Union[str, List[str]]]: + """ + Predicts cDNA priming sites on transcript sequences. + + This tool identifies potential priming sites for a given primer (e.g., oligo-dT) + on transcript sequences, accounting for internal A-rich stretches. + + Args: + transcript_fasta: Path to the FASTA file with transcript sequences. + output_sites: Path to the output file listing predicted priming sites and their probabilities. + primer_seq: The sequence of the primer used for cDNA synthesis. + min_consecutive_a: Minimum number of consecutive 'A's to be considered an internal priming site. + max_mismatches: Maximum allowed mismatches for a priming event. + + Returns: + A dictionary containing the executed command, stdout, stderr, and a list of output files. + """ + # Input validation + if not transcript_fasta.is_file(): + raise FileNotFoundError(f"Transcript FASTA file not found: {transcript_fasta}") + if min_consecutive_a < 1: + raise ValueError("min_consecutive_a must be a positive integer.") + if max_mismatches < 0: + raise ValueError("max_mismatches must be a non-negative integer.") + + cmd = [ + "priming-site-predictor", + "--transcript-fasta", str(transcript_fasta), + "--output-sites", str(output_sites), + "--primer-seq", primer_seq, + "--min-consecutive-a", str(min_consecutive_a), + "--max-mismatches", str(max_mismatches), + ] + + try: + result = subprocess.run( + cmd, + capture_output=True, + text=True, + check=True, + ) + return { + "command_executed": " ".join(cmd), + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": [str(output_sites)], + } + except FileNotFoundError: + raise RuntimeError("priming-site-predictor executable not found. Is scrnasim-toolz installed and in your PATH?") + except subprocess.CalledProcessError as e: + raise RuntimeError( + f"priming-site-predictor failed with exit code {e.returncode}:\n" + f"STDOUT: {e.stdout}\n" + f"STDERR: {e.stderr}" + ) + +@mcp.tool +def cdna_generator( + transcript_fasta: Path, + priming_sites: Path, + structure_counts: Path, + output_cdna_fasta: Path, + seed: Optional[int] = None, +) -> Dict[str, Union[str, List[str]]]: + """ + Generates cDNA sequences by simulating reverse transcription. + + This tool samples priming sites based on their predicted probabilities and generates + the corresponding complementary DNA (cDNA) sequences. + + Args: + transcript_fasta: Path to the FASTA file with transcript sequences. + priming_sites: Path to the file with predicted priming sites. + structure_counts: Path to the file with counts for each transcript structure. + output_cdna_fasta: Path to the output FASTA file for the generated cDNA sequences. + seed: A random seed for reproducibility. + + Returns: + A dictionary containing the executed command, stdout, stderr, and a list of output files. + """ + # Input validation + if not transcript_fasta.is_file(): + raise FileNotFoundError(f"Transcript FASTA file not found: {transcript_fasta}") + if not priming_sites.is_file(): + raise FileNotFoundError(f"Priming sites file not found: {priming_sites}") + if not structure_counts.is_file(): + raise FileNotFoundError(f"Structure counts file not found: {structure_counts}") + + cmd = [ + "cdna-generator", + "--transcript-fasta", str(transcript_fasta), + "--priming-sites", str(priming_sites), + "--structure-counts", str(structure_counts), + "--output-cdna-fasta", str(output_cdna_fasta), + ] + if seed is not None: + cmd.extend(["--seed", str(seed)]) + + try: + result = subprocess.run( + cmd, + capture_output=True, + text=True, + check=True, + ) + return { + "command_executed": " ".join(cmd), + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": [str(output_cdna_fasta)], + } + except FileNotFoundError: + raise RuntimeError("cdna-generator executable not found. Is scrnasim-toolz installed and in your PATH?") + except subprocess.CalledProcessError as e: + raise RuntimeError( + f"cdna-generator failed with exit code {e.returncode}:\n" + f"STDOUT: {e.stdout}\n" + f"STDERR: {e.stderr}" + ) + +@mcp.tool +def fragment_selector( + cdna_fasta: Path, + output_fragment_fasta: Path, + frag_len_mean: int, + frag_len_std: int, + seed: Optional[int] = None, +) -> Dict[str, Union[str, List[str]]]: + """ + Simulates cDNA fragmentation and selects end-fragments. + + This tool fragments the generated cDNA sequences according to a specified length + distribution and selects the fragments corresponding to the ends of the original cDNA. + + Args: + cdna_fasta: Path to the FASTA file with cDNA sequences. + output_fragment_fasta: Path to the output FASTA file for the selected fragments. + frag_len_mean: The mean length for cDNA fragmentation. + frag_len_std: The standard deviation of the fragment length. + seed: A random seed for reproducibility. + + Returns: + A dictionary containing the executed command, stdout, stderr, and a list of output files. + """ + # Input validation + if not cdna_fasta.is_file(): + raise FileNotFoundError(f"cDNA FASTA file not found: {cdna_fasta}") + if frag_len_mean <= 0: + raise ValueError("frag_len_mean must be a positive integer.") + if frag_len_std < 0: + raise ValueError("frag_len_std must be a non-negative integer.") + + cmd = [ + "fragment-selector", + "--cdna-fasta", str(cdna_fasta), + "--output-fragment-fasta", str(output_fragment_fasta), + "--frag-len-mean", str(frag_len_mean), + "--frag-len-std", str(frag_len_std), + ] + if seed is not None: + cmd.extend(["--seed", str(seed)]) + + try: + result = subprocess.run( + cmd, + capture_output=True, + text=True, + check=True, + ) + return { + "command_executed": " ".join(cmd), + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": [str(output_fragment_fasta)], + } + except FileNotFoundError: + raise RuntimeError("fragment-selector executable not found. Is scrnasim-toolz installed and in your PATH?") + except subprocess.CalledProcessError as e: + raise RuntimeError( + f"fragment-selector failed with exit code {e.returncode}:\n" + f"STDOUT: {e.stdout}\n" + f"STDERR: {e.stderr}" + ) + +@mcp.tool +def read_sequencer( + fragment_fasta: Path, + output_reads: Path, + num_reads: int, + read_length: int, + padding_freqs: Optional[str] = None, + seed: Optional[int] = None, +) -> Dict[str, Union[str, List[str]]]: + """ + Generates simulated sequencing reads from cDNA fragments. + + This tool samples fragments and generates reads of a specified length from their 5' ends. + If a fragment is shorter than the read length, it is padded with random nucleotides. + + Args: + fragment_fasta: Path to the FASTA file with cDNA fragments. + output_reads: Path to the output FASTA/FASTQ file for the simulated reads. + num_reads: The total number of reads to sequence. + read_length: The length of each sequencing read. + padding_freqs: Comma-separated nucleotide frequencies for padding, e.g., "A:0.25,C:0.25,G:0.25,T:0.25". + seed: A random seed for reproducibility. + + Returns: + A dictionary containing the executed command, stdout, stderr, and a list of output files. + """ + # Input validation + if not fragment_fasta.is_file(): + raise FileNotFoundError(f"Fragment FASTA file not found: {fragment_fasta}") + if num_reads <= 0: + raise ValueError("num_reads must be a positive integer.") + if read_length <= 0: + raise ValueError("read_length must be a positive integer.") + + cmd = [ + "read-sequencer", + "--fragment-fasta", str(fragment_fasta), + "--output-reads", str(output_reads), + "--num-reads", str(num_reads), + "--read-length", str(read_length), + ] + if padding_freqs: + cmd.extend(["--padding-freqs", padding_freqs]) + if seed is not None: + cmd.extend(["--seed", str(seed)]) + + try: + result = subprocess.run( + cmd, + capture_output=True, + text=True, + check=True, + ) + return { + "command_executed": " ".join(cmd), + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": [str(output_reads)], + } + except FileNotFoundError: + raise RuntimeError("read-sequencer executable not found. Is scrnasim-toolz installed and in your PATH?") + except subprocess.CalledProcessError as e: + raise RuntimeError( + f"read-sequencer failed with exit code {e.returncode}:\n" + f"STDOUT: {e.stdout}\n" + f"STDERR: {e.stderr}" + ) \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_scrnasim-toolz/app/scrnasim-toolz_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_scrnasim-toolz/app/scrnasim-toolz_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..a84535dc3faff3ebb367c256231716da591246db --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_scrnasim-toolz/app/scrnasim-toolz_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_scrnasim-toolz/app/scrnasim-toolz_server.py') +SERVER_NAME = 'biosci_scrnasim_toolz' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_scrnasim-toolz/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_scrnasim-toolz/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..21a7e742da95bc2dee2d0b3df4d2b0e72127505d --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_scrnasim-toolz/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-scrnasim-toolz: + build: . + image: mcp-scrnasim-toolz:latest + container_name: mcp-scrnasim-toolz + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=scrnasim-toolz + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_scrnasim-toolz/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_scrnasim-toolz/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..ab534d34ba18b865b4fbf86a8ab201f4d81fe534 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_scrnasim-toolz/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - scrnasim-toolz + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_scrnasim-toolz/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_scrnasim-toolz/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_scrnasim-toolz/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_searchgui/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_searchgui/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..3ada59a8ae6248eb7f1ac156799e2aa467af27ba --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_searchgui/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install searchgui via conda (e.g., from bioconda) +RUN conda install -c bioconda searchgui -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY app/searchgui_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/searchgui_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/searchgui_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_searchgui/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_searchgui/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_searchgui/app/requirements.txt @@ -0,0 +1 @@ + diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_searchgui/app/searchgui_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_searchgui/app/searchgui_server.py new file mode 100644 index 0000000000000000000000000000000000000000..05fe72b9ce5f6fe2a1303862166cfecd17dec112 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_searchgui/app/searchgui_server.py @@ -0,0 +1,250 @@ +import os +import subprocess +import tempfile +from pathlib import Path +from typing import List, Optional, Dict, Any + +# Assume mcp is imported externally for the @mcp.tool decorator + +class SearchGUIError(Exception): + """Custom exception for SearchGUI errors.""" + pass + +def _execute_searchgui_cli( + main_class: str, + app_args: List[str], + jvm_options: Optional[List[str]] = None, + working_dir: Optional[Path] = None, +) -> Dict[str, Any]: + """ + Helper function to execute SearchGUI command-line interfaces. + + This function assumes 'searchgui' is an executable wrapper available in the system's PATH. + JVM options are passed via the _JAVA_OPTIONS environment variable, which is a common + mechanism for Java applications run via wrapper scripts. + + Args: + main_class: The fully qualified class name of the SearchGUI CLI to run + (e.g., "eu.isas.searchgui.cmd.IdentificationParametersCLI"). + app_args: A list of arguments specific to the SearchGUI CLI command. + jvm_options: Optional list of JVM arguments (e.g., ["-Djava.awt.headless=true"]) + to pass to the Java Virtual Machine. These are set via the + _JAVA_OPTIONS environment variable. + working_dir: Optional working directory for the command execution. + + Returns: + A dictionary containing command execution details. + + Raises: + SearchGUIError: If the command execution fails or the 'searchgui' executable is not found. + """ + command = ["searchgui", main_class] + app_args + + env = os.environ.copy() + if jvm_options: + # Pass JVM options via _JAVA_OPTIONS environment variable. + # This is generally more robust for wrapper scripts than trying to + # insert them directly into the command line after the wrapper name. + env["_JAVA_OPTIONS"] = " ".join(jvm_options) + + try: + process = subprocess.run( + command, + cwd=working_dir, + capture_output=True, + text=True, + check=True, + env=env + ) + stdout = process.stdout + stderr = process.stderr + command_executed = " ".join(command) + + return { + "command_executed": command_executed, + "stdout": stdout, + "stderr": stderr, + "output_files": [], # Specific tools will populate this + } + except subprocess.CalledProcessError as e: + raise SearchGUIError( + f"SearchGUI command failed with exit code {e.returncode}.\n" + f"Command: {' '.join(e.cmd)}\n" + f"Stdout: {e.stdout}\n" + f"Stderr: {e.stderr}" + ) from e + except FileNotFoundError: + raise SearchGUIError( + "The 'searchgui' executable was not found. " + "Please ensure SearchGUI is installed and available in your system's PATH." + ) + +@mcp.tool() +def searchgui_identification_parameters_cli( + output_path: Path, + database_path: Path, + jvm_options: Optional[List[str]] = None, + working_dir: Optional[Path] = None, +) -> Dict[str, Any]: + """ + Configures and runs proteomics identification parameters using SearchGUI's + IdentificationParametersCLI. + + This tool allows specifying the output file for parameters and the database + file to be used for identification. Parameters are inferred from the Docker + example provided in the documentation. + + Args: + output_path: The path to the output file where identification parameters + will be written. The parent directory will be created if it + does not exist. + database_path: The path to the FASTA database file (e.g., UniProt) to be used + for protein identification. Must exist and be a file. + jvm_options: Optional list of JVM arguments (e.g., ["-Djava.awt.headless=true"]) + to pass to the Java Virtual Machine. These are set via the + _JAVA_OPTIONS environment variable. + working_dir: Optional working directory for the command execution. + + Returns: + A dictionary containing command execution details, including stdout, stderr, + and the generated output file. + + Raises: + ValueError: If input paths are invalid. + SearchGUIError: If the SearchGUI command fails. + """ + # Input validation + if not database_path.is_file(): + raise ValueError(f"Database file not found or is not a file: {database_path}") + + # Ensure parent directory for output_path exists + output_path.parent.mkdir(parents=True, exist_ok=True) + + app_args = [ + "-out", str(output_path), + "-db", str(database_path), + ] + + result = _execute_searchgui_cli( + main_class="eu.isas.searchgui.cmd.IdentificationParametersCLI", + app_args=app_args, + jvm_options=jvm_options, + working_dir=working_dir, + ) + + result["output_files"].append(str(output_path)) + return result + +@mcp.tool() +def searchgui_search_cli( + raw_args: Optional[List[str]] = None, + jvm_options: Optional[List[str]] = None, + working_dir: Optional[Path] = None, +) -> Dict[str, Any]: + """ + Executes SearchGUI's SearchCLI for running proteomics search engines. + + Note: Specific parameters for SearchCLI are not detailed in the provided + documentation. Users should consult SearchGUI's official documentation + or run `searchgui eu.isas.searchgui.cmd.SearchCLI -help` for detailed options. + + Args: + raw_args: A list of raw command-line arguments to pass directly to SearchCLI. + Example: ["-in", "input.mgf", "-params", "params.xml", "-out", "results.zip"] + jvm_options: Optional list of JVM arguments (e.g., ["-Djava.awt.headless=true"]) + to pass to the Java Virtual Machine. These are set via the + _JAVA_OPTIONS environment variable. + working_dir: Optional working directory for the command execution. + + Returns: + A dictionary containing command execution details. + + Raises: + SearchGUIError: If the SearchGUI command fails. + """ + app_args = raw_args if raw_args is not None else [] + + result = _execute_searchgui_cli( + main_class="eu.isas.searchgui.cmd.SearchCLI", + app_args=app_args, + jvm_options=jvm_options, + working_dir=working_dir, + ) + # Output files are highly dependent on raw_args, cannot be reliably determined here. + return result + +@mcp.tool() +def searchgui_path_settings_cli( + raw_args: Optional[List[str]] = None, + jvm_options: Optional[List[str]] = None, + working_dir: Optional[Path] = None, +) -> Dict[str, Any]: + """ + Executes SearchGUI's PathSettingsCLI for managing search engine paths. + + Note: Specific parameters for PathSettingsCLI are not detailed in the provided + documentation. Users should consult SearchGUI's official documentation + or run `searchgui eu.isas.searchgui.cmd.PathSettingsCLI -help` for detailed options. + + Args: + raw_args: A list of raw command-line arguments to pass directly to PathSettingsCLI. + Example: ["-xtandem", "/path/to/xtandem.exe"] + jvm_options: Optional list of JVM arguments (e.g., ["-Djava.awt.headless=true"]) + to pass to the Java Virtual Machine. These are set via the + _JAVA_OPTIONS environment variable. + working_dir: Optional working directory for the command execution. + + Returns: + A dictionary containing command execution details. + + Raises: + SearchGUIError: If the SearchGUI command fails. + """ + app_args = raw_args if raw_args is not None else [] + + result = _execute_searchgui_cli( + main_class="eu.isas.searchgui.cmd.PathSettingsCLI", + app_args=app_args, + jvm_options=jvm_options, + working_dir=working_dir, + ) + # This CLI typically modifies settings, not generates files. + return result + +@mcp.tool() +def searchgui_fasta_cli( + raw_args: Optional[List[str]] = None, + jvm_options: Optional[List[str]] = None, + working_dir: Optional[Path] = None, +) -> Dict[str, Any]: + """ + Executes SearchGUI's FastaCLI for FASTA file related operations. + + Note: Specific parameters for FastaCLI are not detailed in the provided + documentation. Users should consult SearchGUI's official documentation + or run `searchgui eu.isas.searchgui.cmd.FastaCLI -help` for detailed options. + + Args: + raw_args: A list of raw command-line arguments to pass directly to FastaCLI. + Example: ["-in", "proteins.fasta", "-out", "filtered.fasta"] + jvm_options: Optional list of JVM arguments (e.g., ["-Djava.awt.headless=true"]) + to pass to the Java Virtual Machine. These are set via the + _JAVA_OPTIONS environment variable. + working_dir: Optional working directory for the command execution. + + Returns: + A dictionary containing command execution details. + + Raises: + SearchGUIError: If the SearchGUI command fails. + """ + app_args = raw_args if raw_args is not None else [] + + result = _execute_searchgui_cli( + main_class="eu.isas.searchgui.cmd.FastaCLI", + app_args=app_args, + jvm_options=jvm_options, + working_dir=working_dir, + ) + # Output files are highly dependent on raw_args, cannot be reliably determined here. + return result \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_searchgui/app/searchgui_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_searchgui/app/searchgui_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..d1e1ab0694927d51df541ceacc2b3fd5dc57b93e --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_searchgui/app/searchgui_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_searchgui/app/searchgui_server.py') +SERVER_NAME = 'biosci_searchgui' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_searchgui/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_searchgui/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..b081897088e926ef4e24d2b3937ce89abb8a180b --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_searchgui/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-searchgui: + build: . + image: mcp-searchgui:latest + container_name: mcp-searchgui + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=searchgui + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_searchgui/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_searchgui/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..e067361739e2b9c58aad412fb1f66c4c9e5a0a8f --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_searchgui/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - searchgui + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_searchgui/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_searchgui/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_searchgui/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_sentieon/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_sentieon/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..b89c074cefc89bbcce968d131a0c3ad6e941c9b1 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_sentieon/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install sentieon via conda (e.g., from bioconda) +RUN conda install -c bioconda sentieon -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY app/sentieon_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/sentieon_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/sentieon_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_sentieon/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_sentieon/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_sentieon/app/requirements.txt @@ -0,0 +1 @@ + diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_sentieon/app/sentieon_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_sentieon/app/sentieon_server.py new file mode 100644 index 0000000000000000000000000000000000000000..bc66e9164fa75c5c09bf423cec33d9e90de9c1e5 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_sentieon/app/sentieon_server.py @@ -0,0 +1,156 @@ +import subprocess +from pathlib import Path +from typing import List, Optional, Dict, Any + +# No mcp import needed as per instructions + +@mcp.tool() +def sentieon_run_command( + subcommand_and_args: List[str], + sentieon_executable: Path = Path("sentieon"), + num_threads: Optional[int] = None, + license_server: Optional[str] = None, + log_file: Optional[Path] = None, + verbose: bool = False, +) -> Dict[str, Any]: + """ + Executes a generic Sentieon command with specified subcommand and arguments. + + WARNING: The provided documentation (conda search --info) does not contain + details about Sentieon's specific subcommands (e.g., 'bwa', 'haplotyper', 'dedup') + or their individual parameters. This function serves as a generic wrapper + for the main 'sentieon' executable. + + To create more specific and type-safe MCP tools for Sentieon's subcommands, + detailed command-line interface documentation (e.g., `sentieon --help` + output or official Sentieon documentation) is required. + + Args: + subcommand_and_args: A list of strings representing the Sentieon subcommand + and its specific arguments. For example: + `["bwa", "-i", "input.fastq", "-o", "output.bam"]` + or `["haplotyper", "-r", "ref.fasta", "-i", "input.bam", "-o", "output.vcf"]`. + This parameter is mandatory and cannot be empty. + sentieon_executable: Path to the Sentieon executable. Defaults to "sentieon" + assuming it's in the system's PATH. The path will be + validated to ensure the executable exists. + num_threads: Number of threads to use for the Sentieon process. + Corresponds to the global `-t` option. Must be a positive integer. + license_server: License server address. Corresponds to the global `-l` option. + e.g., "127.0.0.1:8990" + log_file: Path to a file where Sentieon should write its log messages. + Corresponds to the global `-log` option. The parent directory + will be created if it doesn't exist. + verbose: Enable verbose output. Corresponds to the global `-v` option. + + Returns: + A dictionary containing the command executed, stdout, stderr, and any + identified output files. In this generic function, `output_files` will + typically be empty as specific output paths cannot be inferred. + + Raises: + ValueError: If `subcommand_and_args` is empty or `num_threads` is invalid. + FileNotFoundError: If the `sentieon_executable` cannot be found or resolved. + """ + if not subcommand_and_args: + raise ValueError("subcommand_and_args cannot be empty. Please specify a subcommand and its arguments.") + + # Validate sentieon_executable path + resolved_sentieon_executable = sentieon_executable + if not sentieon_executable.is_absolute(): + try: + # Attempt to resolve the executable path if it's not absolute + which_process = subprocess.run( + ["which", str(sentieon_executable)], + capture_output=True, + text=True, + check=True + ) + resolved_path_str = which_process.stdout.strip() + if not resolved_path_str: + raise FileNotFoundError(f"Sentieon executable '{sentieon_executable}' not found in PATH.") + resolved_sentieon_executable = Path(resolved_path_str) + except (subprocess.CalledProcessError, FileNotFoundError): + # If 'which' fails or the path is not found, check if it's a relative path in current dir + if not sentieon_executable.is_file(): + raise FileNotFoundError(f"Sentieon executable '{sentieon_executable}' not found or not executable. " + "Please ensure it is in your PATH or provide an absolute path.") + elif not sentieon_executable.is_file(): + raise FileNotFoundError(f"Sentieon executable not found at: {sentieon_executable}") + + cmd = [str(resolved_sentieon_executable)] + + # Add global Sentieon options + if num_threads is not None: + if not isinstance(num_threads, int) or num_threads <= 0: + raise ValueError("num_threads must be a positive integer.") + cmd.extend(["-t", str(num_threads)]) + + if license_server: + cmd.extend(["-l", license_server]) + + if log_file: + # Ensure parent directory exists for log file + log_file.parent.mkdir(parents=True, exist_ok=True) + cmd.extend(["-log", str(log_file)]) + + if verbose: + cmd.append("-v") + + # Add the subcommand and its specific arguments + cmd.extend(subcommand_and_args) + + command_str = " ".join(map(str, cmd)) + stdout = "" + stderr = "" + output_files: List[Path] = [] + + try: + process = subprocess.run( + cmd, + capture_output=True, + text=True, + check=True + ) + stdout = process.stdout + stderr = process.stderr + # It is impossible to reliably determine output files without knowing the + # specific subcommand and its output parameters. Users of this generic + # function would need to parse stdout/stderr or know the subcommand's + # behavior to identify outputs. + except subprocess.CalledProcessError as e: + return { + "command_executed": command_str, + "stdout": e.stdout, + "stderr": e.stderr, + "error": f"Sentieon command failed with exit code {e.returncode}: {e.cmd}", + "returncode": e.returncode, + "output_files": output_files, + } + except FileNotFoundError: + return { + "command_executed": command_str, + "stdout": "", + "stderr": f"Error: Sentieon executable not found at '{resolved_sentieon_executable}'. " + "Please ensure it is installed and accessible in your PATH, " + "or provide the correct absolute path.", + "error": "Executable not found", + "returncode": 127, + "output_files": output_files, + } + except Exception as e: + return { + "command_executed": command_str, + "stdout": stdout, + "stderr": stderr, + "error": f"An unexpected error occurred: {str(e)}", + "returncode": 1, # Generic error code for unexpected errors + "output_files": output_files, + } + + return { + "command_executed": command_str, + "stdout": stdout, + "stderr": stderr, + "output_files": output_files, + } \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_sentieon/app/sentieon_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_sentieon/app/sentieon_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..314e919cb5706f402cc17be1f1fb9877c0770849 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_sentieon/app/sentieon_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_sentieon/app/sentieon_server.py') +SERVER_NAME = 'biosci_sentieon' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_sentieon/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_sentieon/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..cc12d821d1f3088ffc73ffbec55c3c1f0f946320 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_sentieon/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-sentieon: + build: . + image: mcp-sentieon:latest + container_name: mcp-sentieon + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=sentieon + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_sentieon/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_sentieon/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..0631d093c2411fe07dfd973e7486008e0256e27e --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_sentieon/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - sentieon + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_sentieon/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_sentieon/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_sentieon/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_seqcluster/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_seqcluster/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_seqcluster/app/requirements.txt @@ -0,0 +1 @@ + diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_seqcluster/app/seqcluster_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_seqcluster/app/seqcluster_server.py new file mode 100644 index 0000000000000000000000000000000000000000..f0402b953ee3e307de3d1c146b5219b535ff75ea --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_seqcluster/app/seqcluster_server.py @@ -0,0 +1,189 @@ +import subprocess +from pathlib import Path +from typing import List, Optional, Dict, Any + +# NOTE: The provided documentation for 'seqcluster' primarily consists of +# metadata and conda package information. It *does not* include the +# command-line interface (CLI) usage details, such as the output of +# `seqcluster --help` or specific subcommands and their parameters. +# +# As a result, this MCP tool implementation is based on *hypothetical* +# parameters and a generic command structure, inferred from the tool's +# summary ("small RNA analysis from NGS data") and common patterns in +# bioinformatics tools. +# +# A complete and accurate MCP tool would require parsing the actual +# CLI documentation to identify all subcommands, their specific parameters, +# types, default values, and constraints. + +@mcp.tool() +def seqcluster_process( + input_files: List[Path], + output_dir: Path = Path("./seqcluster_results"), + min_length: int = 18, + max_length: int = 25, + min_reads: int = 5, + reference_genome: Optional[Path] = None, + threads: int = 1, + dry_run: bool = False, + log_file: Optional[Path] = None, + verbose: bool = False, +) -> Dict[str, Any]: + """ + Processes small RNA sequencing data using the seqcluster tool. + + NOTE: This function uses *hypothetical* parameters due to the absence + of actual command-line documentation for seqcluster in the provided + materials. The parameters are designed to be representative of common + small RNA analysis workflows. + + Args: + input_files: A list of paths to input FASTQ or FASTA files. + At least one file must be provided and exist. + output_dir: Directory to store all output files. Will be created if it doesn't exist. + min_length: Minimum length of small RNA sequences to consider for clustering/analysis. + Must be a positive integer. + max_length: Maximum length of small RNA sequences to consider for clustering/analysis. + Must be a positive integer and greater than or equal to min_length. + min_reads: Minimum number of reads required for a cluster or sequence to be reported. + Must be a positive integer. + reference_genome: Optional path to a reference genome FASTA file for alignment or annotation. + If provided, the file must exist. + threads: Number of CPU threads to use for processing. Must be a positive integer. + dry_run: If True, the command will be constructed and returned, but not executed. + log_file: Optional path to a file where stdout and stderr will be redirected. + verbose: If True, enable verbose output from the seqcluster tool. + + Returns: + A dictionary containing the command executed, stdout, stderr, and a list of output files. + Includes an 'error' key if the command fails. + """ + # Input validation + if not input_files: + raise ValueError("At least one input file must be provided.") + for f in input_files: + if not isinstance(f, Path): + raise TypeError(f"Input file '{f}' must be a Path object.") + if not f.is_file(): + raise FileNotFoundError(f"Input file not found: {f}") + + if not isinstance(output_dir, Path): + raise TypeError("output_dir must be a Path object.") + if not isinstance(min_length, int) or min_length <= 0: + raise ValueError("min_length must be a positive integer.") + if not isinstance(max_length, int) or max_length <= 0: + raise ValueError("max_length must be a positive integer.") + if min_length > max_length: + raise ValueError("min_length cannot be greater than max_length.") + if not isinstance(min_reads, int) or min_reads <= 0: + raise ValueError("min_reads must be a positive integer.") + if not isinstance(threads, int) or threads <= 0: + raise ValueError("threads must be a positive integer.") + + if reference_genome: + if not isinstance(reference_genome, Path): + raise TypeError("reference_genome must be a Path object.") + if not reference_genome.is_file(): + raise FileNotFoundError(f"Reference genome FASTA file not found: {reference_genome}") + + if log_file and not isinstance(log_file, Path): + raise TypeError("log_file must be a Path object.") + + output_dir.mkdir(parents=True, exist_ok=True) + + command = ["seqcluster"] + # Hypothetical common parameters based on typical sRNA analysis tools + command.extend(["--input", *[str(f) for f in input_files]]) + command.extend(["--output-dir", str(output_dir)]) + command.extend(["--min-len", str(min_length)]) + command.extend(["--max-len", str(max_length)]) + command.extend(["--min-reads", str(min_reads)]) + command.extend(["--threads", str(threads)]) + + if reference_genome: + command.extend(["--reference", str(reference_genome)]) + if verbose: + command.append("--verbose") + + command_str = " ".join(command) + if dry_run: + return { + "command_executed": command_str, + "stdout": "Dry run: Command not executed.", + "stderr": "", + "output_files": [], + } + + stdout_data = "" + stderr_data = "" + output_files_generated: List[str] = [] + + try: + if log_file: + with open(log_file, "w") as log_f: + process = subprocess.run( + command, + stdout=log_f, + stderr=log_f, + text=True, + check=True + ) + stdout_data = f"Output redirected to {log_file}" + stderr_data = f"Errors redirected to {log_file}" + else: + process = subprocess.run( + command, + capture_output=True, + text=True, + check=True + ) + stdout_data = process.stdout + stderr_data = process.stderr + + # Hypothetical output files based on common seqcluster outputs + # A real implementation would parse stdout/stderr or consult documentation + # to identify actual output files. + potential_output_files = [ + output_dir / "clusters.tsv", + output_dir / "summary.html", + output_dir / "report.pdf", + ] + if reference_genome: + potential_output_files.append(output_dir / "aligned_reads.bam") + + for f in potential_output_files: + if f.exists(): + output_files_generated.append(str(f)) + + return { + "command_executed": command_str, + "stdout": stdout_data, + "stderr": stderr_data, + "output_files": output_files_generated, + } + except subprocess.CalledProcessError as e: + # If log_file was used, stdout/stderr are in the log file, not e.stdout/e.stderr + if log_file: + with open(log_file, "r") as log_f: + log_content = log_f.read() + stdout_data = f"See log file: {log_file}" + stderr_data = f"See log file: {log_file}\nError details from log:\n{log_content}" + else: + stdout_data = e.stdout + stderr_data = e.stderr + + return { + "command_executed": command_str, + "stdout": stdout_data, + "stderr": stderr_data, + "error": f"seqcluster process failed with exit code {e.returncode}", + "output_files": [], + } + except FileNotFoundError: + return { + "command_executed": command_str, + "stdout": "", + "stderr": "Error: 'seqcluster' command not found. Is it installed and in your PATH?", + "error": "Tool not found", + "output_files": [], + } \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_seqcluster/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_seqcluster/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..7d390b9e1fd3ea601888e051b88eb6e500a2ac3b --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_seqcluster/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-seqcluster: + build: . + image: mcp-seqcluster:latest + container_name: mcp-seqcluster + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=seqcluster + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_seqcluster/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_seqcluster/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..40e7a4081608b00e258e4334356fab9782443ba4 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_seqcluster/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - seqcluster + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_seqfu/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_seqfu/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..2b7f099b28e9329e97ec4b74e635fbb1c44defa4 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_seqfu/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install seqfu via conda (e.g., from bioconda) +RUN conda install -c bioconda seqfu -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY app/seqfu_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/seqfu_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/seqfu_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_seqfu/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_seqfu/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_seqfu/app/requirements.txt @@ -0,0 +1 @@ + diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_seqfu/app/seqfu_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_seqfu/app/seqfu_server.py new file mode 100644 index 0000000000000000000000000000000000000000..bfb010c32136154255ae2f4ea519c2b1c436f5cc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_seqfu/app/seqfu_server.py @@ -0,0 +1,642 @@ +import subprocess +from pathlib import Path +from typing import List, Optional, Union + +@mcp.tool() +def seqfu_bases( + input_files: List[str], +): + """ + Count bases in FASTA/FASTQ files. + + :param input_files: List of paths to FASTA/FASTQ files (can be gzipped). + """ + for f in input_files: + if not Path(f).exists(): + return {"error": f"File not found: {f}"} + + cmd = ["seqfu", "bases"] + input_files + + try: + result = subprocess.run(cmd, capture_output=True, text=True, check=True) + return { + "command_executed": " ".join(cmd), + "stdout": result.stdout, + "stderr": result.stderr, + } + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(cmd), + "error": str(e), + "stdout": e.stdout, + "stderr": e.stderr + } + +@mcp.tool() +def seqfu_check( + input_files: List[str], +): + """ + Check FASTQ files for errors or inconsistencies. + + :param input_files: List of paths to FASTQ files. + """ + for f in input_files: + if not Path(f).exists(): + return {"error": f"File not found: {f}"} + + cmd = ["seqfu", "check"] + input_files + + try: + result = subprocess.run(cmd, capture_output=True, text=True, check=True) + return { + "command_executed": " ".join(cmd), + "stdout": result.stdout, + "stderr": result.stderr, + } + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(cmd), + "error": str(e), + "stdout": e.stdout, + "stderr": e.stderr + } + +@mcp.tool() +def seqfu_count( + input_files: List[str], + is_paired: bool = False, +): + """ + Count FASTA/FASTQ reads, with optional pair-end awareness. + + :param input_files: List of paths to sequence files. + :param is_paired: If True, assume files are paired and count pairs. + """ + for f in input_files: + if not Path(f).exists(): + return {"error": f"File not found: {f}"} + + cmd = ["seqfu", "count"] + if is_paired: + cmd.append("--paired") + cmd.extend(input_files) + + try: + result = subprocess.run(cmd, capture_output=True, text=True, check=True) + return { + "command_executed": " ".join(cmd), + "stdout": result.stdout, + "stderr": result.stderr, + } + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(cmd), + "error": str(e), + "stdout": e.stdout, + "stderr": e.stderr + } + +@mcp.tool() +def seqfu_deinterleave( + input_file: str, + out1: str, + out2: str, +): + """ + Deinterleave a FASTQ file into two separate files (R1 and R2). + + :param input_file: Path to the interleaved FASTQ file. + :param out1: Output path for the first read (R1). + :param out2: Output path for the second read (R2). + """ + if not Path(input_file).exists(): + return {"error": f"Input file not found: {input_file}"} + + cmd = ["seqfu", "deinterleave", "-1", out1, "-2", out2, input_file] + + try: + result = subprocess.run(cmd, capture_output=True, text=True, check=True) + return { + "command_executed": " ".join(cmd), + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": [out1, out2] + } + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(cmd), + "error": str(e), + "stdout": e.stdout, + "stderr": e.stderr + } + +@mcp.tool() +def seqfu_derep( + input_files: List[str], + min_count: int = 1, +): + """ + Dereplicate FASTA/FASTQ files (remove duplicate sequences). + + :param input_files: List of paths to sequence files. + :param min_count: Minimum number of occurrences to keep a sequence. + """ + for f in input_files: + if not Path(f).exists(): + return {"error": f"File not found: {f}"} + + cmd = ["seqfu", "derep", "--min-count", str(min_count)] + input_files + + try: + result = subprocess.run(cmd, capture_output=True, text=True, check=True) + return { + "command_executed": " ".join(cmd), + "stdout": result.stdout, + "stderr": result.stderr, + } + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(cmd), + "error": str(e), + "stdout": e.stdout, + "stderr": e.stderr + } + +@mcp.tool() +def seqfu_interleave( + r1: str, + r2: str, + output_file: Optional[str] = None, +): + """ + Interleave two FASTQ files (R1 and R2) into a single file. + + :param r1: Path to the first read file. + :param r2: Path to the second read file. + :param output_file: Optional path to save the interleaved output. + """ + if not Path(r1).exists() or not Path(r2).exists(): + return {"error": "One or both input files not found."} + + cmd = ["seqfu", "interleave", r1, r2] + + try: + if output_file: + with open(output_file, "w") as out: + result = subprocess.run(cmd, stdout=out, stderr=subprocess.PIPE, text=True, check=True) + return { + "command_executed": " ".join(cmd) + f" > {output_file}", + "stdout": f"Output written to {output_file}", + "stderr": result.stderr, + "output_files": [output_file] + } + else: + result = subprocess.run(cmd, capture_output=True, text=True, check=True) + return { + "command_executed": " ".join(cmd), + "stdout": result.stdout, + "stderr": result.stderr, + } + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(cmd), + "error": str(e), + "stderr": e.stderr + } + +@mcp.tool() +def seqfu_lanes( + input_files: List[str], + output_prefix: str = "merged", +): + """ + Merge Illumina lanes (e.g., L001, L002). + + :param input_files: List of FASTQ files from different lanes. + :param output_prefix: Prefix for the merged output files. + """ + for f in input_files: + if not Path(f).exists(): + return {"error": f"File not found: {f}"} + + cmd = ["seqfu", "lanes", "--prefix", output_prefix] + input_files + + try: + result = subprocess.run(cmd, capture_output=True, text=True, check=True) + return { + "command_executed": " ".join(cmd), + "stdout": result.stdout, + "stderr": result.stderr, + } + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(cmd), + "error": str(e), + "stdout": e.stdout, + "stderr": e.stderr + } + +@mcp.tool() +def seqfu_list( + input_file: str, + list_file: str, +): + """ + Print sequences from a FASTX file that match names in a list. + + :param input_file: Path to the FASTA/FASTQ file. + :param list_file: Path to a text file containing sequence names (one per line). + """ + if not Path(input_file).exists() or not Path(list_file).exists(): + return {"error": "Input file or list file not found."} + + cmd = ["seqfu", "list", "--list", list_file, input_file] + + try: + result = subprocess.run(cmd, capture_output=True, text=True, check=True) + return { + "command_executed": " ".join(cmd), + "stdout": result.stdout, + "stderr": result.stderr, + } + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(cmd), + "error": str(e), + "stdout": e.stdout, + "stderr": e.stderr + } + +@mcp.tool() +def seqfu_metadata( + input_files: List[str], +): + """ + Print a table of FASTQ reads (mapping files/metadata). + + :param input_files: List of paths to FASTQ files. + """ + for f in input_files: + if not Path(f).exists(): + return {"error": f"File not found: {f}"} + + cmd = ["seqfu", "metadata"] + input_files + + try: + result = subprocess.run(cmd, capture_output=True, text=True, check=True) + return { + "command_executed": " ".join(cmd), + "stdout": result.stdout, + "stderr": result.stderr, + } + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(cmd), + "error": str(e), + "stdout": e.stdout, + "stderr": e.stderr + } + +@mcp.tool() +def seqfu_rotate( + input_file: str, + position: int = 1, +): + """ + Rotate a sequence with a new start position. + + :param input_file: Path to the FASTA file. + :param position: The new starting position (1-based). + """ + if not Path(input_file).exists(): + return {"error": f"Input file not found: {input_file}"} + + if position < 1: + return {"error": "Position must be 1 or greater."} + + cmd = ["seqfu", "rotate", "--at", str(position), input_file] + + try: + result = subprocess.run(cmd, capture_output=True, text=True, check=True) + return { + "command_executed": " ".join(cmd), + "stdout": result.stdout, + "stderr": result.stderr, + } + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(cmd), + "error": str(e), + "stdout": e.stdout, + "stderr": e.stderr + } + +@mcp.tool() +def seqfu_sort( + input_files: List[str], + by_size: bool = True, +): + """ + Sort sequences by size. + + :param input_files: List of paths to sequence files. + :param by_size: Sort by sequence length (default True). + """ + for f in input_files: + if not Path(f).exists(): + return {"error": f"File not found: {f}"} + + cmd = ["seqfu", "sort"] + if by_size: + cmd.append("--size") + cmd.extend(input_files) + + try: + result = subprocess.run(cmd, capture_output=True, text=True, check=True) + return { + "command_executed": " ".join(cmd), + "stdout": result.stdout, + "stderr": result.stderr, + } + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(cmd), + "error": str(e), + "stdout": e.stdout, + "stderr": e.stderr + } + +@mcp.tool() +def seqfu_stats( + input_files: List[str], + compact: bool = False, + multi: bool = False, +): + """ + Calculate statistics on sequence lengths (N50, min, max, etc.). + + :param input_files: List of paths to sequence files. + :param compact: Print a compact version of the table. + :param multi: Force multi-file output format. + """ + for f in input_files: + if not Path(f).exists(): + return {"error": f"File not found: {f}"} + + cmd = ["seqfu", "stats"] + if compact: + cmd.append("--compact") + if multi: + cmd.append("--multi") + cmd.extend(input_files) + + try: + result = subprocess.run(cmd, capture_output=True, text=True, check=True) + return { + "command_executed": " ".join(cmd), + "stdout": result.stdout, + "stderr": result.stderr, + } + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(cmd), + "error": str(e), + "stdout": e.stdout, + "stderr": e.stderr + } + +@mcp.tool() +def seqfu_cat( + input_files: List[str], +): + """ + Concatenate FASTA/FASTQ files. + + :param input_files: List of paths to sequence files. + """ + for f in input_files: + if not Path(f).exists(): + return {"error": f"File not found: {f}"} + + cmd = ["seqfu", "cat"] + input_files + + try: + result = subprocess.run(cmd, capture_output=True, text=True, check=True) + return { + "command_executed": " ".join(cmd), + "stdout": result.stdout, + "stderr": result.stderr, + } + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(cmd), + "error": str(e), + "stdout": e.stdout, + "stderr": e.stderr + } + +@mcp.tool() +def seqfu_grep( + pattern: str, + input_files: List[str], + invert_match: bool = False, + count_only: bool = False, +): + """ + Select sequences matching a pattern. + + :param pattern: Regex or string pattern to search for. + :param input_files: List of paths to sequence files. + :param invert_match: Select non-matching sequences. + :param count_only: Only print the count of matching sequences. + """ + for f in input_files: + if not Path(f).exists(): + return {"error": f"File not found: {f}"} + + cmd = ["seqfu", "grep", "-p", pattern] + if invert_match: + cmd.append("-v") + if count_only: + cmd.append("-c") + cmd.extend(input_files) + + try: + result = subprocess.run(cmd, capture_output=True, text=True, check=True) + return { + "command_executed": " ".join(cmd), + "stdout": result.stdout, + "stderr": result.stderr, + } + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(cmd), + "error": str(e), + "stdout": e.stdout, + "stderr": e.stderr + } + +@mcp.tool() +def seqfu_head( + input_files: List[str], + n_sequences: int = 10, +): + """ + Print the first N sequences from a file. + + :param input_files: List of paths to sequence files. + :param n_sequences: Number of sequences to print. + """ + for f in input_files: + if not Path(f).exists(): + return {"error": f"File not found: {f}"} + + cmd = ["seqfu", "head", "-n", str(n_sequences)] + input_files + + try: + result = subprocess.run(cmd, capture_output=True, text=True, check=True) + return { + "command_executed": " ".join(cmd), + "stdout": result.stdout, + "stderr": result.stderr, + } + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(cmd), + "error": str(e), + "stdout": e.stdout, + "stderr": e.stderr + } + +@mcp.tool() +def seqfu_rc( + input_files: List[str], +): + """ + Reverse complement sequences in files. + + :param input_files: List of paths to sequence files. + """ + for f in input_files: + if not Path(f).exists(): + return {"error": f"File not found: {f}"} + + cmd = ["seqfu", "rc"] + input_files + + try: + result = subprocess.run(cmd, capture_output=True, text=True, check=True) + return { + "command_executed": " ".join(cmd), + "stdout": result.stdout, + "stderr": result.stderr, + } + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(cmd), + "error": str(e), + "stdout": e.stdout, + "stderr": e.stderr + } + +@mcp.tool() +def seqfu_tab( + input_files: List[str], + reverse: bool = False, +): + """ + Tabulate reads to TSV (or convert TSV back to FASTX). + + :param input_files: List of paths to sequence files (or TSV files if reverse is True). + :param reverse: Convert TSV back to FASTA/FASTQ. + """ + for f in input_files: + if not Path(f).exists(): + return {"error": f"File not found: {f}"} + + cmd = ["seqfu", "tab"] + if reverse: + cmd.append("--reverse") + cmd.extend(input_files) + + try: + result = subprocess.run(cmd, capture_output=True, text=True, check=True) + return { + "command_executed": " ".join(cmd), + "stdout": result.stdout, + "stderr": result.stderr, + } + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(cmd), + "error": str(e), + "stdout": e.stdout, + "stderr": e.stderr + } + +@mcp.tool() +def seqfu_tail( + input_files: List[str], + n_sequences: int = 10, +): + """ + Print the last N sequences from a file. + + :param input_files: List of paths to sequence files. + :param n_sequences: Number of sequences to print. + """ + for f in input_files: + if not Path(f).exists(): + return {"error": f"File not found: {f}"} + + cmd = ["seqfu", "tail", "-n", str(n_sequences)] + input_files + + try: + result = subprocess.run(cmd, capture_output=True, text=True, check=True) + return { + "command_executed": " ".join(cmd), + "stdout": result.stdout, + "stderr": result.stderr, + } + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(cmd), + "error": str(e), + "stdout": e.stdout, + "stderr": e.stderr + } + +@mcp.tool() +def seqfu_view( + input_files: List[str], + color: bool = True, +): + """ + View sequences with colored quality and oligo matches. + + :param input_files: List of paths to sequence files. + :param color: Enable colored output (default True). + """ + for f in input_files: + if not Path(f).exists(): + return {"error": f"File not found: {f}"} + + cmd = ["seqfu", "view"] + if not color: + cmd.append("--no-color") + cmd.extend(input_files) + + try: + result = subprocess.run(cmd, capture_output=True, text=True, check=True) + return { + "command_executed": " ".join(cmd), + "stdout": result.stdout, + "stderr": result.stderr, + } + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(cmd), + "error": str(e), + "stdout": e.stdout, + "stderr": e.stderr + } \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_seqfu/app/seqfu_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_seqfu/app/seqfu_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..a435c718dc9f799b4782dbd5f03a6b5dfb927e14 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_seqfu/app/seqfu_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_seqfu/app/seqfu_server.py') +SERVER_NAME = 'biosci_seqfu' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_seqfu/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_seqfu/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..33764d11705796d29d0d7386dd242b71e0d23cd7 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_seqfu/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-seqfu: + build: . + image: mcp-seqfu:latest + container_name: mcp-seqfu + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=seqfu + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_seqfu/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_seqfu/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..a2e5fe164ca7d16fa3ca96239598ed8fb717c0d1 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_seqfu/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - seqfu + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_seqfu/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_seqfu/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_seqfu/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_shiba/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_shiba/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..bd158375befb909825159e35404e4e8db4080011 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_shiba/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - shiba + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_snap-aligner/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_snap-aligner/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_snap-aligner/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_snpsift/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_snpsift/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..d37e9dedcded80e6645acbe7ed2d752c9cbb7236 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_snpsift/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install snpsift via conda (e.g., from bioconda) +RUN conda install -c bioconda snpsift -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY app/snpsift_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/snpsift_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/snpsift_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_snpsift/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_snpsift/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_snpsift/app/requirements.txt @@ -0,0 +1 @@ + diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_snpsift/app/snpsift_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_snpsift/app/snpsift_server.py new file mode 100644 index 0000000000000000000000000000000000000000..c64ddfa46093028d4a803e2a6986a49ba9f453c3 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_snpsift/app/snpsift_server.py @@ -0,0 +1,215 @@ +import subprocess +import tempfile +from pathlib import Path +from typing import List, Optional, Union + +# No need to import mcp as per instructions + +@mcp.tool() +def snpsift_run_perl_script( + program_file: Path, + record_separator: Optional[str] = None, + autosplit_mode: bool = False, + unicode_features: Optional[str] = None, + check_syntax_only: bool = False, + run_under_debugger: Optional[str] = None, + debugging_flags: Optional[str] = None, + program_line_e: Optional[List[str]] = None, + program_line_E: Optional[List[str]] = None, + no_site_customize: bool = False, + split_pattern: Optional[str] = None, + edit_in_place_extension: Optional[str] = None, + include_directories: Optional[List[Path]] = None, + line_ending_processing: Optional[str] = None, + modules_to_execute: Optional[List[str]] = None, + assume_while_loop: bool = False, + assume_while_loop_and_print: bool = False, + enable_rudimentary_parsing: bool = False, + look_for_programfile_in_path: bool = False, + enable_tainting_warnings: bool = False, + enable_tainting_checks: bool = False, + dump_core_after_parsing: bool = False, + allow_unsafe_operations: bool = False, + print_version: bool = False, + print_config_variable: Optional[str] = None, + enable_useful_warnings: bool = False, + enable_all_warnings: bool = False, + ignore_text_before_perl_line: Optional[Path] = None, + disable_all_warnings: bool = False, + program_arguments: Optional[List[str]] = None, +) -> dict: + """ + Executes a Perl script with the specified Perl interpreter options. + + NOTE: This tool wraps the generic 'perl' interpreter based on the provided + 'perl -h' documentation. It does NOT expose the specific subcommands or + functionality of the bioinformatics tool 'SnpSift' (which is typically + a Java-based tool for filtering and manipulating annotated files). + The provided documentation for 'snpsift' only contained the help for 'perl -h'. + + Args: + program_file: The path to the Perl program file to execute. + record_separator: Specify record separator (e.g., '\\0', '0777'). + autosplit_mode: Enable autosplit mode with -n or -p (splits $_ into @F). + unicode_features: Enables the listed Unicode features (number or list). + check_syntax_only: Check syntax only (runs BEGIN and CHECK blocks). + run_under_debugger: Run program under debugger (optional debugger name). + debugging_flags: Set debugging flags (argument is a bit mask or alphabets). + program_line_e: One or more lines of program to execute (several -e's allowed). + program_line_E: Like -e, but enables all optional features (several -E's allowed). + no_site_customize: Don't do $sitelib/sitecustomize.pl at startup. + split_pattern: split() pattern for -a switch (e.g., '/pattern/'). + edit_in_place_extension: Edit <> files in place (makes backup if extension supplied). + include_directories: Specify @INC/#include directory (several -I's allowed). + line_ending_processing: Enable line ending processing, specifies line terminator (octal or empty). + modules_to_execute: Execute "use/no module..." before executing program. + Each string in the list should be the full module specifier + (e.g., 'Mstrict', 'm-warnings'). + assume_while_loop: Assume "while (<>) { ... }" loop around program. + assume_while_loop_and_print: Assume loop like -n but print line also, like sed. + enable_rudimentary_parsing: Enable rudimentary parsing for switches after programfile. + look_for_programfile_in_path: Look for programfile using PATH environment variable. + enable_tainting_warnings: Enable tainting warnings. + enable_tainting_checks: Enable tainting checks. + dump_core_after_parsing: Dump core after parsing program. + allow_unsafe_operations: Allow unsafe operations. + print_version: Print version, patchlevel and license. + print_config_variable: Print configuration summary (or a single Config.pm variable). + enable_useful_warnings: Enable many useful warnings. + enable_all_warnings: Enable all warnings. + ignore_text_before_perl_line: Ignore text before #!perl line (optionally cd to directory). + disable_all_warnings: Disable all warnings. + program_arguments: Additional arguments to pass to the Perl script. + """ + # Input validation + if not program_file.is_file(): + raise ValueError(f"Program file not found: {program_file}") + + for directory in include_directories or []: + if not directory.is_dir(): + raise ValueError(f"Include directory not found: {directory}") + + if ignore_text_before_perl_line and not ignore_text_before_perl_line.is_dir(): + raise ValueError(f"Directory for -x option not found: {ignore_text_before_perl_line}") + + command = ["perl"] + + if record_separator is not None: + command.append(f"-0{record_separator}") + if autosplit_mode: + command.append("-a") + if unicode_features is not None: + command.append(f"-C{unicode_features}") + if check_syntax_only: + command.append("-c") + if run_under_debugger is not None: + command.append(f"-d:{run_under_debugger}") + if debugging_flags is not None: + command.append(f"-D{debugging_flags}") + if program_line_e: + for line in program_line_e: + command.append("-e") + command.append(line) + if program_line_E: + for line in program_line_E: + command.append("-E") + command.append(line) + if no_site_customize: + command.append("-f") + if split_pattern is not None: + command.append(f"-F{split_pattern}") + if edit_in_place_extension is not None: + command.append(f"-i{edit_in_place_extension}") + if include_directories: + for directory in include_directories: + command.append("-I") + command.append(str(directory)) + if line_ending_processing is not None: + command.append(f"-l{line_ending_processing}") + if modules_to_execute: + for module_spec in modules_to_execute: + # Validate module_spec format: must start with 'M' or 'm' + if not (module_spec.startswith('M') or module_spec.startswith('m')): + raise ValueError( + f"Invalid module spec for -[mM] switch: '{module_spec}'. " + "Each module specifier must start with 'M' or 'm' " + "(e.g., 'Mstrict', 'm-warnings')." + ) + command.append(f"-{module_spec}") + + if assume_while_loop: + command.append("-n") + if assume_while_loop_and_print: + command.append("-p") + if enable_rudimentary_parsing: + command.append("-s") + if look_for_programfile_in_path: + command.append("-S") + if enable_tainting_warnings: + command.append("-t") + if enable_tainting_checks: + command.append("-T") + if dump_core_after_parsing: + command.append("-u") + if allow_unsafe_operations: + command.append("-U") + if print_version: + command.append("-v") + if print_config_variable is not None: + command.append(f"-V:{print_config_variable}") + if enable_useful_warnings: + command.append("-w") + if enable_all_warnings: + command.append("-W") + if ignore_text_before_perl_line is not None: + command.append(f"-x{ignore_text_before_perl_line}") + if disable_all_warnings: + command.append("-X") + + command.append(str(program_file)) + + if program_arguments: + command.extend(program_arguments) + + command_str = " ".join(map(str, command)) + stdout_output = "" + stderr_output = "" + output_files: List[Path] = [] + + try: + process = subprocess.run( + command, + capture_output=True, + text=True, + check=True + ) + stdout_output = process.stdout + stderr_output = process.stderr + + # If -i is used, the input file is modified in place, so it's an output file. + if edit_in_place_extension is not None: + output_files.append(program_file) + + except subprocess.CalledProcessError as e: + return { + "command_executed": command_str, + "stdout": e.stdout, + "stderr": e.stderr, + "output_files": [], + "error": f"Perl script execution failed with exit code {e.returncode}" + } + except FileNotFoundError: + return { + "command_executed": command_str, + "stdout": "", + "stderr": "Error: 'perl' command not found. Please ensure Perl is installed and in your PATH.", + "output_files": [], + "error": "Perl interpreter not found." + } + + return { + "command_executed": command_str, + "stdout": stdout_output, + "stderr": stderr_output, + "output_files": output_files + } \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_snpsift/app/snpsift_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_snpsift/app/snpsift_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..b305fe32d4c3c2cf1958f3e9d6273fc478f5c1e8 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_snpsift/app/snpsift_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_snpsift/app/snpsift_server.py') +SERVER_NAME = 'biosci_snpsift' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_snpsift/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_snpsift/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..73c68f89eac1ec50b3b57bb568883a1d104a81a2 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_snpsift/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-snpsift: + build: . + image: mcp-snpsift:latest + container_name: mcp-snpsift + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=snpsift + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_snpsift/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_snpsift/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..ebc98671a8317d5b6aa5708f55864c0645a37337 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_snpsift/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - snpsift + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_snpsift/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_snpsift/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_snpsift/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_sortmerna/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_sortmerna/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..d11aca2dad859bced701780a54459eb92cbf2aa8 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_sortmerna/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install sortmerna via conda (e.g., from bioconda) +RUN conda install -c bioconda sortmerna -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY app/sortmerna_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/sortmerna_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/sortmerna_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_sortmerna/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_sortmerna/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_sortmerna/app/requirements.txt @@ -0,0 +1 @@ + diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_sortmerna/app/sortmerna_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_sortmerna/app/sortmerna_server.py new file mode 100644 index 0000000000000000000000000000000000000000..f4d7e71fa71abe269f3f9dc4b856a86450280aeb --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_sortmerna/app/sortmerna_server.py @@ -0,0 +1,208 @@ +import subprocess +from pathlib import Path +from typing import List, Optional +import tempfile +import os + +@mcp.tool() +def sortmerna( + ref: List[str], + reads: List[str], + workdir: str = "sortmerna_work", + kvdb: Optional[str] = None, + idx_dir: Optional[str] = None, + threads: int = 1, + fastx: bool = False, + aligned: Optional[str] = None, + other: Optional[str] = None, + blast: Optional[str] = None, + num_alignments: int = 1, + best: int = 1, + paired_in: bool = False, + paired_out: bool = False, + out_order: bool = False, + readfeed: int = 1, + verbose: bool = False, +): + """ + SortMeRNA: A biological sequence analysis tool for filtering, mapping and OTU-picking NGS reads. + Mainly used for filtering rRNA from metatranscriptomic data. + + Args: + ref: List of paths to reference database files (e.g. silva-bac-16s.fasta). Required. + reads: List of paths to read files (fasta/fastq/gz). For paired-end, provide two files. Required. + workdir: Directory for all temporary and output files. + kvdb: Directory for RocksDB key-value storage (defaults to workdir/kvdb). + idx_dir: Directory for reference indexes (defaults to workdir/idx). + threads: Number of threads to use. + fastx: If True, output aligned and/or rejected reads in FASTA/FASTQ format. + aligned: File prefix for aligned (rRNA) reads. + other: File prefix for rejected (non-rRNA) reads. + blast: Output alignments in BLAST-like format. Use '0' for pairwise, '1' for tabular. + num_alignments: Report first N alignments per read. + best: Report best N alignments per read. + paired_in: If True, flags paired-end reads as interleaved in a single file. + paired_out: If True, output paired-end reads into two separate files. + out_order: If True, maintain the same order of reads in output as in input. + readfeed: Splitting behavior (v5.0.0+). 1 enables multithreaded parallel processing. + verbose: Enable verbose output. + """ + # Input validation + if not ref: + return {"error": "At least one reference file (--ref) must be provided."} + if not reads: + return {"error": "At least one reads file (--reads) must be provided."} + + for r in ref: + if not Path(r).exists(): + return {"error": f"Reference file not found: {r}"} + for rd in reads: + if not Path(rd).exists(): + return {"error": f"Reads file not found: {rd}"} + + if threads < 1: + threads = 1 + + # Construct command + cmd = ["sortmerna"] + + # Add multiple references + for r in ref: + cmd.extend(["--ref", str(Path(r).absolute())]) + + # Add multiple reads + for rd in reads: + cmd.extend(["--reads", str(Path(rd).absolute())]) + + # Paths + cmd.extend(["--workdir", str(Path(workdir).absolute())]) + if kvdb: + cmd.extend(["--kvdb", str(Path(kvdb).absolute())]) + if idx_dir: + cmd.extend(["--idx-dir", str(Path(idx_dir).absolute())]) + + # Options + cmd.extend(["--threads", str(threads)]) + cmd.extend(["--num_alignments", str(num_alignments)]) + cmd.extend(["--best", str(best)]) + cmd.extend(["--readfeed", str(readfeed)]) + + if fastx: + cmd.append("--fastx") + if aligned: + cmd.extend(["--aligned", aligned]) + if other: + cmd.extend(["--other", other]) + if blast is not None: + cmd.extend(["--blast", str(blast)]) + if paired_in: + cmd.append("--paired_in") + if paired_out: + cmd.append("--paired_out") + if out_order: + cmd.append("--out_order") + if verbose: + cmd.append("-v") + + try: + # Ensure workdir exists + Path(workdir).mkdir(parents=True, exist_ok=True) + + result = subprocess.run(cmd, capture_output=True, text=True, check=True) + + # Collect output files from workdir/out + output_path = Path(workdir) / "out" + output_files = [] + if output_path.exists(): + output_files = [str(f) for f in output_path.glob("*") if f.is_file()] + + return { + "command_executed": " ".join(cmd), + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": output_files, + "status": "success" + } + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(cmd), + "stdout": e.stdout, + "stderr": e.stderr, + "error": str(e), + "status": "failed" + } + except Exception as e: + return {"error": str(e), "status": "failed"} + +@mcp.tool() +def sortmerna_split( + files: List[str], + num_splits: int = 8, + workdir: str = "sortmerna_split_out", +): + """ + Utility to perform multithreaded splitting of large sequence files prior to SortMeRNA processing. + Uses rapidgzip and pigz internally (if available in the environment). + + Args: + files: List of sequence files to split. + num_splits: Number of chunks to split the files into. + workdir: Working directory to store split chunks and descriptors. + """ + if not files: + return {"error": "No files provided for splitting."} + + for f in files: + if not Path(f).exists(): + return {"error": f"File not found: {f}"} + + if num_splits < 1: + num_splits = 1 + + # Based on the documentation: python run.py split --file --num-splits --workdir + # Note: This assumes 'run.py' is available or the logic is accessible via the sortmerna environment. + # Since run.py is a helper script in the repo, we attempt to call it via python. + + cmd = ["python", "run.py", "split"] + for f in files: + cmd.extend(["--file", str(Path(f).absolute())]) + + cmd.extend(["--num-splits", str(num_splits)]) + cmd.extend(["--workdir", str(Path(workdir).absolute())]) + + try: + Path(workdir).mkdir(parents=True, exist_ok=True) + result = subprocess.run(cmd, capture_output=True, text=True, check=True) + + output_files = [str(f) for f in Path(workdir).glob("*") if f.is_file()] + + return { + "command_executed": " ".join(cmd), + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": output_files, + "status": "success" + } + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(cmd), + "stdout": e.stdout, + "stderr": e.stderr, + "error": "Ensure run.py from the SortMeRNA repository is in the current directory or PYTHONPATH.", + "status": "failed" + } + except Exception as e: + return {"error": str(e), "status": "failed"} + +@mcp.tool() +def sortmerna_version(): + """Check the installed version of SortMeRNA.""" + try: + result = subprocess.run(["sortmerna", "--version"], capture_output=True, text=True, check=True) + return { + "command_executed": "sortmerna --version", + "stdout": result.stdout, + "stderr": result.stderr + } + except subprocess.CalledProcessError as e: + return {"error": str(e), "stderr": e.stderr} \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_sortmerna/app/sortmerna_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_sortmerna/app/sortmerna_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..8e8733831d1026c870e78f93b7d2662fb82f7f95 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_sortmerna/app/sortmerna_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_sortmerna/app/sortmerna_server.py') +SERVER_NAME = 'biosci_sortmerna' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_sortmerna/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_sortmerna/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..dfef85ea23710958b5259ac9c183292c21b1de96 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_sortmerna/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-sortmerna: + build: . + image: mcp-sortmerna:latest + container_name: mcp-sortmerna + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=sortmerna + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_sortmerna/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_sortmerna/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..c4378a424a790e5594b44266f282f16c0f3274e8 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_sortmerna/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - sortmerna + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_sortmerna/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_sortmerna/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_sortmerna/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_subread/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_subread/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..b97a577a9a5954e0f1131f7d9a10ae19f173bc49 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_subread/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install subread via conda (e.g., from bioconda) +RUN conda install -c bioconda subread -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY app/subread_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/subread_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/subread_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_subread/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_subread/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_subread/app/requirements.txt @@ -0,0 +1 @@ + diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_subread/app/subread_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_subread/app/subread_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..c72746e07edd0725f524bf4ae9aa6ba49e8cdb06 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_subread/app/subread_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_subread/app/subread_server.py') +SERVER_NAME = 'biosci_subread' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_subread/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_subread/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..2a72be4099bbf2c7dd6a45f8962ce9367c62d310 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_subread/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-subread: + build: . + image: mcp-subread:latest + container_name: mcp-subread + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=subread + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_subread/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_subread/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..50fdad61e2e1694d33e81c1c4075e3ab0f881e55 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_subread/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - subread + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_subread/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_subread/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_subread/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_tabix/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_tabix/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..927298ae7c7ef6904600460b0a9b64ca1b57e0dc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_tabix/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install tabix via conda (e.g., from bioconda) +RUN conda install -c bioconda tabix -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY app/tabix_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/tabix_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/tabix_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_tabix/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_tabix/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_tabix/app/requirements.txt @@ -0,0 +1 @@ + diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_tabix/app/tabix_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_tabix/app/tabix_server.py new file mode 100644 index 0000000000000000000000000000000000000000..d0a8e956b8ed20fbeff95567a9e86d4335163e8f --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_tabix/app/tabix_server.py @@ -0,0 +1,189 @@ +import subprocess +from pathlib import Path +from typing import List, Optional, Literal + +@mcp.tool() +def tabix( + in_tab_bgz: Path, + regions: Optional[List[str]] = None, + preset: Literal["gff", "bed", "sam", "vcf", "psltbl"] = "gff", + sequence_name_column: int = 1, + start_column: int = 4, + end_column: int = 5, + skip_first_lines: int = 0, + comment_char: str = "#", + replace_header_file: Optional[Path] = None, + bed_regions: bool = False, + zero_based_coordinate: bool = False, + print_header_lines: bool = False, + print_only_header_lines: bool = False, + list_chromosome_names: bool = False, + force_overwrite_index: bool = False, +) -> dict: + """ + Indexes a TAB-delimited file or retrieves regions from an indexed file. + + This tool can create a tabix index (.tbi or .csi) for a BGZF-compressed, + tab-delimited file, or query specific genomic regions from an already + indexed file. + + Args: + in_tab_bgz: Path to the input BGZF-compressed, tab-delimited file. + Must exist and be readable. + regions: A list of genomic regions to query (e.g., ["chr1:100-200", "chr2"]). + If not provided, tabix will attempt to create an index for the file. + preset: Format preset for indexing. Choose from "gff", "bed", "sam", "vcf", "psltbl". + Defaults to "gff". + sequence_name_column: Column for the sequence name (e.g., chromosome). 1-based. + Defaults to 1. + start_column: Column for the start coordinate. 1-based. Defaults to 4. + end_column: Column for the end coordinate. 1-based. Can be identical to start_column. + Defaults to 5. + skip_first_lines: Skip the first INT lines. Defaults to 0. + comment_char: Symbol for comment/meta lines. Defaults to "#". Must be a single character. + replace_header_file: Path to a file whose content will replace the header of the input file. + If provided, must exist and be readable. + bed_regions: Treat the first region argument as a BED file containing regions to query. + If True, `regions` must contain at least one path to a BED file. + zero_based_coordinate: Use zero-based coordinates for start/end columns. Defaults to False. + print_header_lines: Print header lines along with data lines when querying. Defaults to False. + print_only_header_lines: Print only the header lines. Mutually exclusive with `print_header_lines`. + Defaults to False. + list_chromosome_names: List chromosome names present in the file. Mutually exclusive with + region queries and index creation options. Defaults to False. + force_overwrite_index: Force to overwrite an existing index file. Defaults to False. + + Returns: + A dictionary containing the command executed, stdout, stderr, and any output files. + """ + # Input validation + if not in_tab_bgz.is_file(): + raise FileNotFoundError(f"Input file not found: {in_tab_bgz}") + + if replace_header_file: + if not replace_header_file.is_file(): + raise FileNotFoundError(f"Header replacement file not found: {replace_header_file}") + + if not (1 <= sequence_name_column): + raise ValueError("Sequence name column must be 1 or greater.") + if not (1 <= start_column): + raise ValueError("Start column must be 1 or greater.") + if not (1 <= end_column): + raise ValueError("End column must be 1 or greater.") + if not (0 <= skip_first_lines): + raise ValueError("Number of lines to skip must be non-negative.") + + if len(comment_char) != 1: + raise ValueError("Comment character must be a single character.") + + # Mutual exclusivity checks + if print_header_lines and print_only_header_lines: + raise ValueError("Options -h (print_header_lines) and -H (print_only_header_lines) are mutually exclusive.") + + if list_chromosome_names: + if regions: + raise ValueError("Cannot specify regions when listing chromosome names (-l).") + if bed_regions: + raise ValueError("Cannot specify BED regions when listing chromosome names (-l).") + # While other indexing options (preset, columns, etc.) might be technically allowed, + # they are usually irrelevant for -l and might lead to unexpected behavior or be ignored. + # The current implementation allows them, but tabix might ignore them. + + if bed_regions: + if not regions: + raise ValueError("If 'bed_regions' is True, 'regions' must be provided and contain at least one BED file path.") + # The help states "region1 is a BED file". This implies the first element of `regions` is a path. + if not Path(regions[0]).is_file(): + raise FileNotFoundError(f"The first region specified for BED regions (-B) must be a valid BED file path: {regions[0]}") + + command = ["tabix"] + + # Add options + if preset != "gff": # Only add if not default + command.extend(["-p", preset]) + if sequence_name_column != 1: + command.extend(["-s", str(sequence_name_column)]) + if start_column != 4: + command.extend(["-b", str(start_column)]) + if end_column != 5: + command.extend(["-e", str(end_column)]) + if skip_first_lines != 0: + command.extend(["-S", str(skip_first_lines)]) + if comment_char != "#": + command.extend(["-c", comment_char]) + if replace_header_file: + command.extend(["-r", str(replace_header_file)]) + if bed_regions: + command.append("-B") + if zero_based_coordinate: + command.append("-0") + if print_header_lines: + command.append("-h") + if print_only_header_lines: + command.append("-H") + if list_chromosome_names: + command.append("-l") + if force_overwrite_index: + command.append("-f") + + # Add input file + command.append(str(in_tab_bgz)) + + # Add regions + if regions: + command.extend(regions) + + command_str = " ".join(command) + stdout = "" + stderr = "" + output_files = [] + + try: + process = subprocess.run( + command, + capture_output=True, + text=True, + check=True + ) + stdout = process.stdout + stderr = process.stderr + + # Determine if an index file was created/overwritten. + # This happens if no regions are specified (indexing mode) or if -f is used. + # The index file is typically .tbi or .csi. + # For tabix 0.2.5, .tbi is the standard. + # If regions are provided, it's a query, not an index creation. + if not regions or force_overwrite_index: + # Check for .tbi index + index_file_tbi = in_tab_bgz.with_suffix(in_tab_bgz.suffix + ".tbi") + if index_file_tbi.exists(): + output_files.append(str(index_file_tbi)) + else: + # Check for .csi index (used by newer htslib/tabix for larger chromosomes) + index_file_csi = in_tab_bgz.with_suffix(in_tab_bgz.suffix + ".csi") + if index_file_csi.exists(): + output_files.append(str(index_file_csi)) + + except subprocess.CalledProcessError as e: + return { + "command_executed": command_str, + "stdout": e.stdout, + "stderr": e.stderr, + "output_files": [], + "error": f"tabix command failed with exit code {e.returncode}" + } + except FileNotFoundError: + return { + "command_executed": command_str, + "stdout": "", + "stderr": "tabix command not found. Is it installed and in your PATH?", + "output_files": [], + "error": "tabix executable not found." + } + + return { + "command_executed": command_str, + "stdout": stdout, + "stderr": stderr, + "output_files": output_files + } \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_tabix/app/tabix_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_tabix/app/tabix_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..3072cd5373e0805060634dbf1914074f08d38985 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_tabix/app/tabix_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_tabix/app/tabix_server.py') +SERVER_NAME = 'biosci_tabix' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_tabix/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_tabix/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..53ab6a2ea5390fc22bc88972319f506779b8c6b5 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_tabix/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-tabix: + build: . + image: mcp-tabix:latest + container_name: mcp-tabix + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=tabix + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_tabix/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_tabix/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..cb95ca11f53710b290ec4898530d3a991d1afd16 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_tabix/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - tabix + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_tabix/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_tabix/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_tabix/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_talon/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_talon/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..b5bd5e8f318ea57317d28e48f83befca9efe5842 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_talon/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install talon via conda (e.g., from bioconda) +RUN conda install -c bioconda talon -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY app/talon_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/talon_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/talon_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_talon/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_talon/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_talon/app/requirements.txt @@ -0,0 +1 @@ + diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_talon/app/talon_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_talon/app/talon_server.py new file mode 100644 index 0000000000000000000000000000000000000000..36e5effec0f063110b428f4344857372bac9f446 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_talon/app/talon_server.py @@ -0,0 +1,537 @@ +import subprocess +import logging +from pathlib import Path +from typing import Optional, List + +# Set up logging +logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') + +# This is a placeholder for the MCP decorator. +# In a real MCP environment, this would be provided by the MCP framework. +class mcp: + @staticmethod + def tool(): + def decorator(func): + return func + return decorator + +@mcp.tool() +def talon_initialize_database( + fasta: Path, + gtf: Path, + annotation: str, + l: int = 500, + idprefix: str = "TALON", + outprefix: str = "talon" +) -> dict: + """ + Initializes a TALON database from a reference genome FASTA and a GTF annotation. + + Args: + fasta: Path to the reference genome FASTA file. + gtf: Path to the reference genome GTF file. + annotation: Name for the annotation being processed. + l: Length of the 5-prime novelty buffer in bases. Default is 500. + idprefix: Prefix for all TALON-generated gene and transcript IDs. Default is 'TALON'. + outprefix: Prefix for output files. The database will be named .db. Default is 'talon'. + + Returns: + A dictionary containing the command executed, stdout, stderr, and a list of output files. + """ + # Input validation + if not fasta.is_file(): + raise FileNotFoundError(f"Input FASTA file not found: {fasta}") + if not gtf.is_file(): + raise FileNotFoundError(f"Input GTF file not found: {gtf}") + if l < 0: + raise ValueError("Novelty buffer length 'l' must be a non-negative integer.") + + output_db = Path(f"{outprefix}.db") + + cmd = [ + "talon_initialize_database", + "--f", str(fasta), + "--g", str(gtf), + "--a", annotation, + "--l", str(l), + "--idprefix", idprefix, + "--o", outprefix + ] + + try: + process = subprocess.run( + cmd, + check=True, + capture_output=True, + text=True + ) + return { + "command_executed": " ".join(cmd), + "stdout": process.stdout, + "stderr": process.stderr, + "output_files": [str(output_db.resolve())] + } + except subprocess.CalledProcessError as e: + logging.error(f"Error executing talon_initialize_database: {e.stderr}") + return { + "command_executed": " ".join(cmd), + "stdout": e.stdout, + "stderr": e.stderr, + "error": "TALON command failed", + "return_code": e.returncode + } + +@mcp.tool() +def talon_label_reads( + sam_file: Path, + db: Path, + threads: int = 1, + max_internal_prime: int = 50, + outprefix: str = "talon" +) -> dict: + """ + Labels reads in a SAM file based on a TALON database. + + Args: + sam_file: Path to the SAM file of reads aligned to the genome. + db: Path to the TALON database file. + threads: Number of threads to use. Default is 1. + max_internal_prime: Max distance allowed between read ends and transcript ends + to consider the read an internal prime. Default is 50. + outprefix: Prefix for output files. Default is 'talon'. + + Returns: + A dictionary containing the command executed, stdout, stderr, and a list of output files. + """ + # Input validation + if not sam_file.is_file(): + raise FileNotFoundError(f"Input SAM file not found: {sam_file}") + if not db.is_file(): + raise FileNotFoundError(f"TALON database file not found: {db}") + if threads <= 0: + raise ValueError("Number of threads must be a positive integer.") + if max_internal_prime < 0: + raise ValueError("max_internal_prime must be a non-negative integer.") + + output_sam = Path(f"{outprefix}_labeled.sam") + + cmd = [ + "talon_label_reads", + "--f", str(sam_file), + "--d", str(db), + "--t", str(threads), + "--ar", str(max_internal_prime), + "--o", outprefix + ] + + try: + process = subprocess.run( + cmd, + check=True, + capture_output=True, + text=True + ) + return { + "command_executed": " ".join(cmd), + "stdout": process.stdout, + "stderr": process.stderr, + "output_files": [str(output_sam.resolve())] + } + except subprocess.CalledProcessError as e: + logging.error(f"Error executing talon_label_reads: {e.stderr}") + return { + "command_executed": " ".join(cmd), + "stdout": e.stdout, + "stderr": e.stderr, + "error": "TALON command failed", + "return_code": e.returncode + } + +@mcp.tool() +def talon( + config: Path, + db: Path, + build: Optional[str] = None, + min_coverage: float = 0.9, + min_identity: float = 0.8, + outprefix: str = "talon" +) -> dict: + """ + Runs the main TALON tool to identify novel genes and transcripts from long read data. + + Args: + config: Path to a file indicating sample, platform, and SAM file location for each input. + db: Path to the TALON database file. + build: Genome build name (e.g., 'hg38'). + min_coverage: Minimum query coverage to accept a read. Default is 0.9. + min_identity: Minimum query identity to accept a read. Default is 0.8. + outprefix: Prefix for output files. Default is 'talon'. + + Returns: + A dictionary containing the command executed, stdout, stderr, and a list of output files. + """ + # Input validation + if not config.is_file(): + raise FileNotFoundError(f"Input config file not found: {config}") + if not db.is_file(): + raise FileNotFoundError(f"TALON database file not found: {db}") + if not (0.0 <= min_coverage <= 1.0): + raise ValueError("min_coverage must be between 0.0 and 1.0.") + if not (0.0 <= min_identity <= 1.0): + raise ValueError("min_identity must be between 0.0 and 1.0.") + + cmd = [ + "talon", + "--f", str(config), + "--db", str(db), + "--cov", str(min_coverage), + "--identity", str(min_identity), + "--o", outprefix + ] + if build: + cmd.extend(["--build", build]) + + output_files = [ + str(Path(f"{outprefix}_talon.log").resolve()), + str(Path(f"{outprefix}_unfiltered_transcripts.tsv").resolve()), + str(Path(f"{outprefix}_read_annot.tsv").resolve()), + str(Path(f"{outprefix}_abundance.tsv").resolve()) + ] + + try: + process = subprocess.run( + cmd, + check=True, + capture_output=True, + text=True + ) + return { + "command_executed": " ".join(cmd), + "stdout": process.stdout, + "stderr": process.stderr, + "output_files": output_files + } + except subprocess.CalledProcessError as e: + logging.error(f"Error executing talon: {e.stderr}") + return { + "command_executed": " ".join(cmd), + "stdout": e.stdout, + "stderr": e.stderr, + "error": "TALON command failed", + "return_code": e.returncode + } + +@mcp.tool() +def talon_summarize( + db: Path, + outprefix: str = "talon" +) -> dict: + """ + Generates a summary of the contents of a TALON database. + + Args: + db: Path to the TALON database file. + outprefix: Prefix for output files. Default is 'talon'. + + Returns: + A dictionary containing the command executed, stdout, stderr, and a list of output files. + """ + # Input validation + if not db.is_file(): + raise FileNotFoundError(f"TALON database file not found: {db}") + + output_summary = Path(f"{outprefix}_summary.tsv") + + cmd = [ + "talon_summarize", + "--db", str(db), + "--o", outprefix + ] + + try: + process = subprocess.run( + cmd, + check=True, + capture_output=True, + text=True + ) + return { + "command_executed": " ".join(cmd), + "stdout": process.stdout, + "stderr": process.stderr, + "output_files": [str(output_summary.resolve())] + } + except subprocess.CalledProcessError as e: + logging.error(f"Error executing talon_summarize: {e.stderr}") + return { + "command_executed": " ".join(cmd), + "stdout": e.stdout, + "stderr": e.stderr, + "error": "TALON command failed", + "return_code": e.returncode + } + +@mcp.tool() +def talon_filter_transcripts( + db: Path, + annotation: Optional[str] = None, + datasets: Optional[str] = None, + max_frac_a: float = 0.5, + min_count: int = 1, + min_datasets: int = 1, + outprefix: str = "talon" +) -> dict: + """ + Filters transcripts based on read support and other quality metrics. + + Args: + db: Path to the TALON database file. + annotation: Annotation name. If not provided, all annotations are used. + datasets: Comma-separated list of datasets to include. If not provided, all are used. + max_frac_a: Maximum fraction of A's downstream of the 3' end. Default is 0.5. + min_count: Minimum number of reads supporting a transcript. Default is 1. + min_datasets: Minimum number of datasets a transcript must be observed in. Default is 1. + outprefix: Prefix for output files. Default is 'talon'. + + Returns: + A dictionary containing the command executed, stdout, stderr, and a list of output files. + """ + # Input validation + if not db.is_file(): + raise FileNotFoundError(f"TALON database file not found: {db}") + if not (0.0 <= max_frac_a <= 1.0): + raise ValueError("max_frac_a must be between 0.0 and 1.0.") + if min_count < 1: + raise ValueError("min_count must be at least 1.") + if min_datasets < 1: + raise ValueError("min_datasets must be at least 1.") + + cmd = [ + "talon_filter_transcripts", + "--db", str(db), + "--maxFracA", str(max_frac_a), + "--minCount", str(min_count), + "--minDatasets", str(min_datasets), + "--o", outprefix + ] + if annotation: + cmd.extend(["--a", annotation]) + if datasets: + cmd.extend(["--datasets", datasets]) + + output_files = [ + str(Path(f"{outprefix}_talon_whitelist.csv").resolve()), + str(Path(f"{outprefix}_talon_filtered_transcripts.tsv").resolve()) + ] + + try: + process = subprocess.run( + cmd, + check=True, + capture_output=True, + text=True + ) + return { + "command_executed": " ".join(cmd), + "stdout": process.stdout, + "stderr": process.stderr, + "output_files": output_files + } + except subprocess.CalledProcessError as e: + logging.error(f"Error executing talon_filter_transcripts: {e.stderr}") + return { + "command_executed": " ".join(cmd), + "stdout": e.stdout, + "stderr": e.stderr, + "error": "TALON command failed", + "return_code": e.returncode + } + +@mcp.tool() +def talon_abundance( + db: Path, + annotation: str, + datasets: Optional[str] = None, + build: Optional[str] = None, + outprefix: str = "talon" +) -> dict: + """ + Computes abundance estimates for genes and transcripts. + + Args: + db: Path to the TALON database file. + annotation: Annotation name to use for the analysis. + datasets: Comma-separated list of datasets to include. If not provided, all are used. + build: Genome build name. + outprefix: Prefix for output files. Default is 'talon'. + + Returns: + A dictionary containing the command executed, stdout, stderr, and a list of output files. + """ + # Input validation + if not db.is_file(): + raise FileNotFoundError(f"TALON database file not found: {db}") + + cmd = [ + "talon_abundance", + "--db", str(db), + "-a", annotation, + "--o", outprefix + ] + if datasets: + cmd.extend(["--d", datasets]) + if build: + cmd.extend(["--build", build]) + + output_files = [ + str(Path(f"{outprefix}_abundance.tsv").resolve()), + str(Path(f"{outprefix}_read_annot.tsv").resolve()), + str(Path(f"{outprefix}_gene_abundance.tsv").resolve()), + str(Path(f"{outprefix}_transcript_abundance.tsv").resolve()) + ] + + try: + process = subprocess.run( + cmd, + check=True, + capture_output=True, + text=True + ) + return { + "command_executed": " ".join(cmd), + "stdout": process.stdout, + "stderr": process.stderr, + "output_files": output_files + } + except subprocess.CalledProcessError as e: + logging.error(f"Error executing talon_abundance: {e.stderr}") + return { + "command_executed": " ".join(cmd), + "stdout": e.stdout, + "stderr": e.stderr, + "error": "TALON command failed", + "return_code": e.returncode + } + +@mcp.tool() +def talon_create_gtf( + db: Path, + annotation: str, + build: Optional[str] = None, + datasets: Optional[str] = None, + outprefix: str = "talon" +) -> dict: + """ + Creates a GTF file of novel transcripts from a TALON database. + + Args: + db: Path to the TALON database file. + annotation: Annotation name to use for the analysis. + build: Genome build name. + datasets: Comma-separated list of datasets to include. If not provided, all are used. + outprefix: Prefix for output files. Default is 'talon'. + + Returns: + A dictionary containing the command executed, stdout, stderr, and a list of output files. + """ + # Input validation + if not db.is_file(): + raise FileNotFoundError(f"TALON database file not found: {db}") + + output_gtf = Path(f"{outprefix}_talon.gtf") + + cmd = [ + "talon_create_GTF", + "--db", str(db), + "-a", annotation, + "--o", outprefix + ] + if build: + cmd.extend(["--build", build]) + if datasets: + cmd.extend(["--datasets", datasets]) + + try: + process = subprocess.run( + cmd, + check=True, + capture_output=True, + text=True + ) + return { + "command_executed": " ".join(cmd), + "stdout": process.stdout, + "stderr": process.stderr, + "output_files": [str(output_gtf.resolve())] + } + except subprocess.CalledProcessError as e: + logging.error(f"Error executing talon_create_GTF: {e.stderr}") + return { + "command_executed": " ".join(cmd), + "stdout": e.stdout, + "stderr": e.stderr, + "error": "TALON command failed", + "return_code": e.returncode + } + +@mcp.tool() +def talon_get_sjs( + db: Path, + annotation: Optional[str] = None, + build: Optional[str] = None, + datasets: Optional[str] = None, + outprefix: str = "talon" +) -> dict: + """ + Outputs a splice junction file from a TALON database. + + Args: + db: Path to the TALON database file. + annotation: Annotation name to use. If not provided, all annotations are used. + build: Genome build name. + datasets: Comma-separated list of datasets to include. If not provided, all are used. + outprefix: Prefix for output files. Default is 'talon'. + + Returns: + A dictionary containing the command executed, stdout, stderr, and a list of output files. + """ + # Input validation + if not db.is_file(): + raise FileNotFoundError(f"TALON database file not found: {db}") + + output_sj = Path(f"{outprefix}_talon_SJ.tsv") + + cmd = [ + "talon_get_sjs", + "--db", str(db), + "--o", outprefix + ] + if annotation: + cmd.extend(["-a", annotation]) + if build: + cmd.extend(["--build", build]) + if datasets: + cmd.extend(["--datasets", datasets]) + + try: + process = subprocess.run( + cmd, + check=True, + capture_output=True, + text=True + ) + return { + "command_executed": " ".join(cmd), + "stdout": process.stdout, + "stderr": process.stderr, + "output_files": [str(output_sj.resolve())] + } + except subprocess.CalledProcessError as e: + logging.error(f"Error executing talon_get_sjs: {e.stderr}") + return { + "command_executed": " ".join(cmd), + "stdout": e.stdout, + "stderr": e.stderr, + "error": "TALON command failed", + "return_code": e.returncode + } \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_talon/app/talon_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_talon/app/talon_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..c895ff34c901a37bd1d2deb70784d207f92e4156 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_talon/app/talon_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_talon/app/talon_server.py') +SERVER_NAME = 'biosci_talon' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_talon/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_talon/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..db90803a9a64b26de10c64f16ceb0703ece84c6e --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_talon/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-talon: + build: . + image: mcp-talon:latest + container_name: mcp-talon + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=talon + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_talon/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_talon/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..7e91d48b06e08c4b3b47c96bcffdc23d9451899a --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_talon/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - talon + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_talon/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_talon/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_talon/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_trimmomatic/app/trimmomatic_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_trimmomatic/app/trimmomatic_server.py new file mode 100644 index 0000000000000000000000000000000000000000..dc04c2654cd205b0a9cde1d1dc8f6fa3f8306677 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_trimmomatic/app/trimmomatic_server.py @@ -0,0 +1,320 @@ +import subprocess +from pathlib import Path +from typing import Optional, List, Dict, Any + +@mcp.tool() +def trimmomatic_pe( + input_forward: Optional[str] = None, + input_reverse: Optional[str] = None, + output_forward_paired: Optional[str] = None, + output_forward_unpaired: Optional[str] = None, + output_reverse_paired: Optional[str] = None, + output_reverse_unpaired: Optional[str] = None, + basein: Optional[str] = None, + baseout: Optional[str] = None, + threads: int = 0, + phred: str = "phred33", + trimlog: Optional[str] = None, + summary: Optional[str] = None, + validate_pairs: bool = False, + compress_level: Optional[int] = None, + compression_mode: Optional[str] = None, + quiet: bool = False, + illuminaclip: Optional[str] = None, + leading: Optional[int] = None, + trailing: Optional[int] = None, + headcrop: Optional[int] = None, + tailcrop: Optional[int] = None, + crop: Optional[int] = None, + slidingwindow: Optional[str] = None, + maxinfo: Optional[str] = None, + maxlen: Optional[int] = None, + minlen: Optional[int] = None, + avgqual: Optional[int] = None, + basecount: Optional[str] = None, + tophred33: bool = False, + tophred64: bool = False, +) -> Dict[str, Any]: + """ + Run Trimmomatic in Paired-End (PE) mode to trim and crop Illumina FASTQ data. + + Args: + input_forward: Path to the forward input FASTQ file. + input_reverse: Path to the reverse input FASTQ file. + output_forward_paired: Path for forward reads that remain paired. + output_forward_unpaired: Path for forward reads that become unpaired. + output_reverse_paired: Path for reverse reads that remain paired. + output_reverse_unpaired: Path for reverse reads that become unpaired. + basein: Template input file (mate is auto-detected). Alternative to input_forward/reverse. + baseout: Template output file (generates 4 files with _1P, _1U, _2P, _2U suffixes). + threads: Number of CPU threads to use (0 = auto-detect). + phred: Quality score encoding ('phred33' or 'phred64'). + trimlog: Path to write a detailed log file. + summary: Path to write a summary of trimming results. + validate_pairs: Perform extra validation to ensure read pairs are consistent. + compress_level: Compression level for BZIP2/GZ output (1-9). + compression_mode: Compression mode ('Stream' or 'Block'). + quiet: Suppress progress output to console. + illuminaclip: Adapter clipping string 'fasta:seedMismatches:palindromeClipThreshold:simpleClipThreshold[:minAdapterLength:keepBothReads]'. + leading: Cut bases off the start if below this quality. + trailing: Cut bases off the end if below this quality. + headcrop: Number of bases to remove from the start. + tailcrop: Number of bases to remove from the end. + crop: Number of bases to keep from the start. + slidingwindow: Sliding window trimming 'windowSize:requiredQuality'. + maxinfo: Maximize useful information 'targetLength:strictness'. + maxlen: Discard reads longer than this. + minlen: Discard reads shorter than this. + avgqual: Discard reads with average quality below this. + basecount: Discard reads based on base frequency 'bases:minCount:maxCount'. + tophred33: Convert quality scores to Phred-33. + tophred64: Convert quality scores to Phred-64. + """ + cmd = ["trimmomatic", "PE"] + + # Global Options + if threads > 0: + cmd.extend(["-threads", str(threads)]) + + if phred == "phred64": + cmd.append("-phred64") + else: + cmd.append("-phred33") + + if trimlog: + cmd.extend(["-trimlog", trimlog]) + + if summary: + cmd.extend(["-summary", summary]) + + if validate_pairs: + cmd.append("-validatePairs") + + if compress_level is not None: + if not (1 <= compress_level <= 9): + raise ValueError("compress_level must be between 1 and 9") + cmd.extend(["-compressLevel", str(compress_level)]) + + if compression_mode: + if compression_mode.lower() == "stream": + cmd.append("-compressStream") + elif compression_mode.lower() == "block": + cmd.append("-compressBlock") + + if quiet: + cmd.append("-quiet") + + # Input/Output Handling + if basein: + if not Path(basein).exists(): + raise FileNotFoundError(f"basein file not found: {basein}") + cmd.extend(["-basein", basein]) + else: + if not input_forward or not input_reverse: + raise ValueError("Either 'basein' or both 'input_forward' and 'input_reverse' must be provided.") + if not Path(input_forward).exists(): + raise FileNotFoundError(f"Input forward file not found: {input_forward}") + if not Path(input_reverse).exists(): + raise FileNotFoundError(f"Input reverse file not found: {input_reverse}") + cmd.extend([input_forward, input_reverse]) + + if baseout: + cmd.extend(["-baseout", baseout]) + else: + if not all([output_forward_paired, output_forward_unpaired, output_reverse_paired, output_reverse_unpaired]): + raise ValueError("Either 'baseout' or all four output file paths must be provided.") + cmd.extend([output_forward_paired, output_forward_unpaired, output_reverse_paired, output_reverse_unpaired]) + + # Trimming Steps (Order matters) + steps = [] + if illuminaclip: + steps.append(f"ILLUMINACLIP:{illuminaclip}") + if tophred33: + steps.append("TOPHRED33") + if tophred64: + steps.append("TOPHRED64") + if leading is not None: + steps.append(f"LEADING:{leading}") + if trailing is not None: + steps.append(f"TRAILING:{trailing}") + if headcrop is not None: + steps.append(f"HEADCROP:{headcrop}") + if tailcrop is not None: + steps.append(f"TAILCROP:{tailcrop}") + if crop is not None: + steps.append(f"CROP:{crop}") + if slidingwindow: + steps.append(f"SLIDINGWINDOW:{slidingwindow}") + if maxinfo: + steps.append(f"MAXINFO:{maxinfo}") + if avgqual is not None: + steps.append(f"AVGQUAL:{avgqual}") + if basecount: + steps.append(f"BASECOUNT:{basecount}") + if minlen is not None: + steps.append(f"MINLEN:{minlen}") + if maxlen is not None: + steps.append(f"MAXLEN:{maxlen}") + + if not steps: + raise ValueError("At least one trimming step must be specified.") + + cmd.extend(steps) + + try: + result = subprocess.run(cmd, capture_output=True, text=True, check=True) + return { + "command_executed": " ".join(cmd), + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": [output_forward_paired, output_forward_unpaired, output_reverse_paired, output_reverse_unpaired] if not baseout else [baseout] + } + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(cmd), + "stdout": e.stdout, + "stderr": e.stderr, + "error": str(e) + } + +@mcp.tool() +def trimmomatic_se( + input_file: str, + output_file: str, + threads: int = 0, + phred: str = "phred33", + trimlog: Optional[str] = None, + summary: Optional[str] = None, + compress_level: Optional[int] = None, + compression_mode: Optional[str] = None, + quiet: bool = False, + illuminaclip: Optional[str] = None, + leading: Optional[int] = None, + trailing: Optional[int] = None, + headcrop: Optional[int] = None, + tailcrop: Optional[int] = None, + crop: Optional[int] = None, + slidingwindow: Optional[str] = None, + maxinfo: Optional[str] = None, + maxlen: Optional[int] = None, + minlen: Optional[int] = None, + avgqual: Optional[int] = None, + basecount: Optional[str] = None, + tophred33: bool = False, + tophred64: bool = False, +) -> Dict[str, Any]: + """ + Run Trimmomatic in Single-End (SE) mode to trim and crop Illumina FASTQ data. + + Args: + input_file: Path to the input FASTQ file. + output_file: Path for the processed output FASTQ file. + threads: Number of CPU threads to use (0 = auto-detect). + phred: Quality score encoding ('phred33' or 'phred64'). + trimlog: Path to write a detailed log file. + summary: Path to write a summary of trimming results. + compress_level: Compression level for BZIP2/GZ output (1-9). + compression_mode: Compression mode ('Stream' or 'Block'). + quiet: Suppress progress output to console. + illuminaclip: Adapter clipping string 'fasta:seedMismatches:palindromeClipThreshold:simpleClipThreshold'. + leading: Cut bases off the start if below this quality. + trailing: Cut bases off the end if below this quality. + headcrop: Number of bases to remove from the start. + tailcrop: Number of bases to remove from the end. + crop: Number of bases to keep from the start. + slidingwindow: Sliding window trimming 'windowSize:requiredQuality'. + maxinfo: Maximize useful information 'targetLength:strictness'. + maxlen: Discard reads longer than this. + minlen: Discard reads shorter than this. + avgqual: Discard reads with average quality below this. + basecount: Discard reads based on base frequency 'bases:minCount:maxCount'. + tophred33: Convert quality scores to Phred-33. + tophred64: Convert quality scores to Phred-64. + """ + cmd = ["trimmomatic", "SE"] + + # Global Options + if threads > 0: + cmd.extend(["-threads", str(threads)]) + + if phred == "phred64": + cmd.append("-phred64") + else: + cmd.append("-phred33") + + if trimlog: + cmd.extend(["-trimlog", trimlog]) + + if summary: + cmd.extend(["-summary", summary]) + + if compress_level is not None: + if not (1 <= compress_level <= 9): + raise ValueError("compress_level must be between 1 and 9") + cmd.extend(["-compressLevel", str(compress_level)]) + + if compression_mode: + if compression_mode.lower() == "stream": + cmd.append("-compressStream") + elif compression_mode.lower() == "block": + cmd.append("-compressBlock") + + if quiet: + cmd.append("-quiet") + + # Input/Output + if not Path(input_file).exists(): + raise FileNotFoundError(f"Input file not found: {input_file}") + cmd.extend([input_file, output_file]) + + # Trimming Steps + steps = [] + if illuminaclip: + steps.append(f"ILLUMINACLIP:{illuminaclip}") + if tophred33: + steps.append("TOPHRED33") + if tophred64: + steps.append("TOPHRED64") + if leading is not None: + steps.append(f"LEADING:{leading}") + if trailing is not None: + steps.append(f"TRAILING:{trailing}") + if headcrop is not None: + steps.append(f"HEADCROP:{headcrop}") + if tailcrop is not None: + steps.append(f"TAILCROP:{tailcrop}") + if crop is not None: + steps.append(f"CROP:{crop}") + if slidingwindow: + steps.append(f"SLIDINGWINDOW:{slidingwindow}") + if maxinfo: + steps.append(f"MAXINFO:{maxinfo}") + if avgqual is not None: + steps.append(f"AVGQUAL:{avgqual}") + if basecount: + steps.append(f"BASECOUNT:{basecount}") + if minlen is not None: + steps.append(f"MINLEN:{minlen}") + if maxlen is not None: + steps.append(f"MAXLEN:{maxlen}") + + if not steps: + raise ValueError("At least one trimming step must be specified.") + + cmd.extend(steps) + + try: + result = subprocess.run(cmd, capture_output=True, text=True, check=True) + return { + "command_executed": " ".join(cmd), + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": [output_file] + } + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(cmd), + "stdout": e.stdout, + "stderr": e.stderr, + "error": str(e) + } diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_trimmomatic/app/trimmomatic_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_trimmomatic/app/trimmomatic_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..b7cd2e0e935d06a46d43227dc387eb7a7cc5f4d8 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_trimmomatic/app/trimmomatic_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_trimmomatic/app/trimmomatic_server.py') +SERVER_NAME = 'biosci_trimmomatic' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_trimmomatic/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_trimmomatic/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..be243db143f3911a5a43b47c1318aac907367ea9 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_trimmomatic/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - trimmomatic + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_trnascan-se/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_trnascan-se/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..65ceb6bc0ee9df9508ab051f6fde05325f0f6c4b --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_trnascan-se/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install trnascan-se via conda (e.g., from bioconda) +RUN conda install -c bioconda trnascan-se -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY app/trnascan-se_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/trnascan-se_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/trnascan-se_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_trnascan-se/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_trnascan-se/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_trnascan-se/app/requirements.txt @@ -0,0 +1 @@ + diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_trnascan-se/app/trnascan-se_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_trnascan-se/app/trnascan-se_server.py new file mode 100644 index 0000000000000000000000000000000000000000..8906d21fbf92a22926e44638161df03835d3ed11 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_trnascan-se/app/trnascan-se_server.py @@ -0,0 +1,288 @@ +from pathlib import Path +import subprocess +from typing import Optional, List, Dict, Any + +@mcp.tool() +def trnascan_se( + input_fasta: str, + output_file: Optional[str] = None, + struct_file: Optional[str] = None, + fasta_output: Optional[str] = None, + stats_file: Optional[str] = None, + bed_file: Optional[str] = None, + organism_type: str = "eukaryotic", + search_engine: str = "infernal", + score_cutoff: float = 0.0, + threads: int = 1, + quiet: bool = False, + detail: bool = False, + brief: bool = False, + show_progress: bool = False, + force_overwrite: bool = False, +): + """ + tRNAscan-SE: tRNA detection in large-scale genomic sequences. + + Args: + input_fasta: Input genome sequence file in FASTA format. + output_file: Save results to this file (default: stdout). + struct_file: Save tRNA secondary structures to this file. + fasta_output: Save tRNA sequences in FASTA format to this file. + stats_file: Save statistics to this file. + bed_file: Save tRNA hits in BED format to this file. + organism_type: Search mode. Options: eukaryotic, bacterial, archaeal, mitochondrial, organellar, general. + search_engine: Search engine to use. Options: infernal (default), legacy, cove, trnascan. + score_cutoff: Global score cutoff (default uses tool defaults based on organism type). + threads: Number of threads to use for Infernal search (default: 1). + quiet: Quiet mode (suppress banner and progress). + detail: Show detailed output. + brief: Brief output format (no column headers). + show_progress: Show progress of search. + force_overwrite: Force overwrite of existing output files. + """ + # Input validation + input_path = Path(input_fasta) + if not input_path.exists(): + return {"error": f"Input file not found: {input_fasta}"} + + cmd = ["tRNAscan-SE"] + + # Organism Mode Mapping + mode_map = { + "eukaryotic": "-E", + "bacterial": "-B", + "archaeal": "-A", + "mitochondrial": "-M", + "organellar": "-O", + "general": "-G" + } + if organism_type.lower() in mode_map: + cmd.append(mode_map[organism_type.lower()]) + else: + return {"error": f"Invalid organism_type. Must be one of: {list(mode_map.keys())}"} + + # Search Engine Mapping + engine_map = { + "infernal": "--infernal", + "legacy": "--legacy", + "cove": "--cove", + "trnascan": "--trnascan" + } + if search_engine.lower() in engine_map: + cmd.append(engine_map[search_engine.lower()]) + else: + return {"error": f"Invalid search_engine. Must be one of: {list(engine_map.keys())}"} + + # Output Files + output_files_created = [] + if output_file: + cmd.extend(["-o", output_file]) + output_files_created.append(output_file) + if struct_file: + cmd.extend(["-f", struct_file]) + output_files_created.append(struct_file) + if fasta_output: + cmd.extend(["-a", fasta_output]) + output_files_created.append(fasta_output) + if stats_file: + cmd.extend(["-m", stats_file]) + output_files_created.append(stats_file) + if bed_file: + cmd.extend(["-b", bed_file]) + output_files_created.append(bed_file) + + # Advanced Options + if score_cutoff > 0: + cmd.extend(["-p", str(score_cutoff)]) + + if threads > 1: + cmd.extend(["--thread", str(threads)]) + + if quiet: + cmd.append("-q") + if detail: + cmd.append("-d") + if brief: + cmd.append("--brief") + if show_progress: + cmd.append("-y") + if force_overwrite: + # tRNAscan-SE doesn't have a direct --force, but we check if files exist + pass + + # Positional argument + cmd.append(str(input_path)) + + try: + result = subprocess.run(cmd, capture_output=True, text=True, check=True) + return { + "command_executed": " ".join(cmd), + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": output_files_created + } + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(cmd), + "error": str(e), + "stdout": e.stdout, + "stderr": e.stderr + } + +@mcp.tool() +def trnascan_se_decode( + input_file: str, + output_file: Optional[str] = None, + mode: str = "eukaryotic" +): + """ + Utility to decode tRNAscan-SE output into a more readable format or specific sub-types. + + Args: + input_file: tRNAscan-SE output file. + output_file: Output file path. + mode: Organism mode used for the original search. + """ + input_path = Path(input_file) + if not input_path.exists(): + return {"error": f"Input file not found: {input_file}"} + + # Note: tRNAscan-SE-decode is often a perl script included in the suite + cmd = ["tRNAscan-SE-decode"] + + mode_flag = "-E" + if mode.lower() == "bacterial": mode_flag = "-B" + elif mode.lower() == "archaeal": mode_flag = "-A" + + cmd.append(mode_flag) + cmd.append(str(input_path)) + + try: + result = subprocess.run(cmd, capture_output=True, text=True, check=True) + + if output_file: + with open(output_file, "w") as f: + f.write(result.stdout) + return { + "command_executed": " ".join(cmd), + "stdout": "Results written to " + output_file, + "output_files": [output_file] + } + + return { + "command_executed": " ".join(cmd), + "stdout": result.stdout, + "stderr": result.stderr + } + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(cmd), + "error": str(e), + "stderr": e.stderr + } + +@mcp.tool() +def eufindtrna( + input_fasta: str, + output_file: Optional[str] = None, + score_cutoff: float = -21.44, + is_intermediate: bool = False, + relaxed_mode: bool = False +): + """ + EufindtRNA: Search for eukaryotic tRNAs using the EufindtRNA algorithm (part of tRNAscan-SE suite). + + Args: + input_fasta: Input FASTA file. + output_file: Output file for hits. + score_cutoff: Log-odds score cutoff (default: -21.44). + is_intermediate: Output in intermediate format for tRNAscan-SE. + relaxed_mode: Use relaxed parameters for search. + """ + input_path = Path(input_fasta) + if not input_path.exists(): + return {"error": f"Input file not found: {input_fasta}"} + + cmd = ["EufindtRNA"] + + if score_cutoff != -21.44: + cmd.extend(["-s", str(score_cutoff)]) + if is_intermediate: + cmd.append("-i") + if relaxed_mode: + cmd.append("-r") + + cmd.append(str(input_path)) + + try: + result = subprocess.run(cmd, capture_output=True, text=True, check=True) + + if output_file: + with open(output_file, "w") as f: + f.write(result.stdout) + return { + "command_executed": " ".join(cmd), + "stdout": "Results written to " + output_file, + "output_files": [output_file] + } + + return { + "command_executed": " ".join(cmd), + "stdout": result.stdout, + "stderr": result.stderr + } + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(cmd), + "error": str(e), + "stderr": e.stderr + } + +@mcp.tool() +def cove_only_search( + input_fasta: str, + model_file: str, + output_file: Optional[str] = None, + score_cutoff: float = 20.0 +): + """ + Run a Cove-only search (legacy component of tRNAscan-SE) using a specific covariance model. + + Args: + input_fasta: Input FASTA file. + model_file: Cove covariance model file. + output_file: Output file for results. + score_cutoff: Score cutoff for hits (default: 20.0). + """ + input_path = Path(input_fasta) + model_path = Path(model_file) + + if not input_path.exists() or not model_path.exists(): + return {"error": "Input FASTA or Model file not found."} + + # cove-search is the underlying binary + cmd = ["cove-search", "-T", str(score_cutoff), str(model_path), str(input_path)] + + try: + result = subprocess.run(cmd, capture_output=True, text=True, check=True) + + if output_file: + with open(output_file, "w") as f: + f.write(result.stdout) + return { + "command_executed": " ".join(cmd), + "stdout": "Results written to " + output_file, + "output_files": [output_file] + } + + return { + "command_executed": " ".join(cmd), + "stdout": result.stdout, + "stderr": result.stderr + } + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(cmd), + "error": str(e), + "stderr": e.stderr + } \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_trnascan-se/app/trnascan-se_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_trnascan-se/app/trnascan-se_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..28cb6e77612d0cd8eb20705e009efcb2830199e0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_trnascan-se/app/trnascan-se_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_trnascan-se/app/trnascan-se_server.py') +SERVER_NAME = 'biosci_trnascan_se' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_trnascan-se/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_trnascan-se/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..179d74dc832482a2abb4a17b1aceaec9a120aa24 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_trnascan-se/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-trnascan-se: + build: . + image: mcp-trnascan-se:latest + container_name: mcp-trnascan-se + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=trnascan-se + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_trnascan-se/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_trnascan-se/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..8cf0a0247b8e4987242be4f34e2cfa331797732a --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_trnascan-se/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - trnascan-se + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_trnascan-se/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_trnascan-se/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_trnascan-se/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_ucsc-bedgraphtobigwig/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_ucsc-bedgraphtobigwig/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_ucsc-bedgraphtobigwig/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_ucsc-gtftogenepred/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_ucsc-gtftogenepred/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_ucsc-gtftogenepred/app/requirements.txt @@ -0,0 +1 @@ + diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_ucsc-gtftogenepred/app/ucsc-gtftogenepred_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_ucsc-gtftogenepred/app/ucsc-gtftogenepred_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..4ff4f1ac1dcbc6240bd6ba2a848784cb7a7cd125 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_ucsc-gtftogenepred/app/ucsc-gtftogenepred_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_ucsc-gtftogenepred/app/ucsc-gtftogenepred_server.py') +SERVER_NAME = 'biosci_ucsc_gtftogenepred' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_ucsc-gtftogenepred/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_ucsc-gtftogenepred/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..c5c85f65bd2fcedff7c2801c431de17bf3d9f3cb --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_ucsc-gtftogenepred/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-ucsc-gtftogenepred: + build: . + image: mcp-ucsc-gtftogenepred:latest + container_name: mcp-ucsc-gtftogenepred + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=ucsc-gtftogenepred + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_ucsc-gtftogenepred/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_ucsc-gtftogenepred/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..23af7011c42fbad6fe1f648b56a7759e11f36a23 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_ucsc-gtftogenepred/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - ucsc-gtftogenepred + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_ucsc-gtftogenepred/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_ucsc-gtftogenepred/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_ucsc-gtftogenepred/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_ucsc-wigtobigwig/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_ucsc-wigtobigwig/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_ucsc-wigtobigwig/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_vcftools/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_vcftools/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..76185cbba3688f1621bc23307f4ff3906fd672e9 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_vcftools/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install vcftools via conda (e.g., from bioconda) +RUN conda install -c bioconda vcftools -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY app/vcftools_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/vcftools_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/vcftools_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_vcftools/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_vcftools/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_vcftools/app/requirements.txt @@ -0,0 +1 @@ + diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_vcftools/app/vcftools_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_vcftools/app/vcftools_server.py new file mode 100644 index 0000000000000000000000000000000000000000..27ef9aee27edf89a793bbf3c423855af7909b8e6 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_vcftools/app/vcftools_server.py @@ -0,0 +1,414 @@ +import subprocess +from pathlib import Path +from typing import Optional, List, Union +import tempfile + +def _get_input_flag(input_path: str) -> str: + """Helper to determine the correct input flag based on file extension.""" + path = Path(input_path) + if path.suffix == ".gz": + if ".vcf" in path.name: + return "--gzvcf" + if path.suffix == ".bcf": + return "--bcf" + return "--vcf" + +@mcp.tool() +def vcftools_filter_and_recode( + input_file: str, + output_prefix: str, + chromosome: Optional[str] = None, + from_bp: Optional[int] = None, + to_bp: Optional[int] = None, + min_q: float = 0.0, + min_maf: float = 0.0, + max_maf: float = 1.0, + max_missing: float = 0.0, + min_mean_dp: float = 0.0, + max_mean_dp: float = 999999.0, + min_alleles: int = 0, + max_alleles: int = 100, + remove_indels: bool = False, + recode: bool = True, + recode_info_all: bool = False, +) -> dict: + """ + Filter a VCF file based on various criteria and optionally output a new VCF file. + + Args: + input_file: Path to the input VCF/VCF.GZ/BCF file. + output_prefix: Prefix for the output files. + chromosome: Only process sites on this chromosome. + from_bp: Lower bound for range filtering (requires chromosome). + to_bp: Upper bound for range filtering (requires chromosome). + min_q: Minimum quality score (Phred scale). + min_maf: Minimum Minor Allele Frequency. + max_maf: Maximum Minor Allele Frequency. + max_missing: Exclude sites on the basis of the proportion of missing data (0 to 1). + min_mean_dp: Minimum average depth for a site. + max_mean_dp: Maximum average depth for a site. + min_alleles: Minimum number of alleles. + max_alleles: Maximum number of alleles. + remove_indels: Exclude all sites that contain an indel. + recode: If True, generate a new VCF file. + recode_info_all: If True, keep all INFO fields in the recoded VCF. + """ + input_path = Path(input_file) + if not input_path.exists(): + return {"error": f"Input file {input_file} not found."} + + input_flag = _get_input_flag(input_file) + cmd = ["vcftools", input_flag, input_file, "--out", output_prefix] + + if chromosome: + cmd.extend(["--chr", chromosome]) + if from_bp is not None: + cmd.extend(["--from-bp", str(from_bp)]) + if to_bp is not None: + cmd.extend(["--to-bp", str(to_bp)]) + + if min_q > 0: + cmd.extend(["--minQ", str(min_q)]) + if min_maf > 0: + cmd.extend(["--maf", str(min_maf)]) + if max_maf < 1.0: + cmd.extend(["--max-maf", str(max_maf)]) + if max_missing > 0: + cmd.extend(["--max-missing", str(max_missing)]) + if min_mean_dp > 0: + cmd.extend(["--min-meanDP", str(min_mean_dp)]) + if max_mean_dp < 999999.0: + cmd.extend(["--max-meanDP", str(max_mean_dp)]) + if min_alleles > 0: + cmd.extend(["--min-alleles", str(min_alleles)]) + if max_alleles < 100: + cmd.extend(["--max-alleles", str(max_alleles)]) + if remove_indels: + cmd.append("--remove-indels") + + if recode: + cmd.append("--recode") + if recode_info_all: + cmd.append("--recode-INFO-all") + + try: + result = subprocess.run(cmd, capture_output=True, text=True, check=True) + # VCFtools usually outputs to files with the prefix, but logs to stderr + output_files = list(Path(".").glob(f"{output_prefix}*")) + return { + "command_executed": " ".join(cmd), + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": [str(f) for f in output_files] + } + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(cmd), + "error": str(e), + "stdout": e.stdout, + "stderr": e.stderr + } + +@mcp.tool() +def vcftools_calculate_statistics( + input_file: str, + output_prefix: str, + stat_type: str, + window_size: int = 0, + window_step: int = 0, +) -> dict: + """ + Calculate various population genetics statistics from a VCF file. + + Args: + input_file: Path to the input VCF/VCF.GZ/BCF file. + output_prefix: Prefix for the output files. + stat_type: Type of statistic to calculate. Options: + 'freq' (allele frequency), + 'counts' (allele counts), + 'depth' (mean depth per individual), + 'site-depth' (depth per site), + 'site-mean-depth' (mean depth per site), + 'site-pi' (nucleotide diversity per site), + 'window-pi' (nucleotide diversity in windows), + 'tajima-d' (Tajima's D), + 'het' (heterozygosity), + 'hardy' (Hardy-Weinberg Equilibrium p-value), + 'missing-indv' (missingness per individual), + 'missing-site' (missingness per site). + window_size: Size of the window for windowed statistics (e.g., window-pi, tajima-d). + window_step: Step size between windows. + """ + input_path = Path(input_file) + if not input_path.exists(): + return {"error": f"Input file {input_file} not found."} + + input_flag = _get_input_flag(input_file) + cmd = ["vcftools", input_flag, input_file, "--out", output_prefix] + + stat_map = { + "freq": "--freq", + "counts": "--counts", + "depth": "--depth", + "site-depth": "--site-depth", + "site-mean-depth": "--site-mean-depth", + "site-pi": "--site-pi", + "window-pi": "--window-pi", + "tajima-d": "--TajimaD", + "het": "--het", + "hardy": "--hardy", + "missing-indv": "--missing-indv", + "missing-site": "--missing-site" + } + + if stat_type not in stat_map: + return {"error": f"Invalid stat_type. Choose from: {list(stat_map.keys())}"} + + if stat_type == "window-pi": + if window_size <= 0: + return {"error": "window_size must be > 0 for window-pi"} + cmd.extend([stat_map[stat_type], str(window_size)]) + if window_step > 0: + cmd.extend(["--window-pi-step", str(window_step)]) + elif stat_type == "tajima-d": + if window_size <= 0: + return {"error": "window_size must be > 0 for tajima-d"} + cmd.extend([stat_map[stat_type], str(window_size)]) + else: + cmd.append(stat_map[stat_type]) + + try: + result = subprocess.run(cmd, capture_output=True, text=True, check=True) + output_files = list(Path(".").glob(f"{output_prefix}*")) + return { + "command_executed": " ".join(cmd), + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": [str(f) for f in output_files] + } + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(cmd), + "error": str(e), + "stdout": e.stdout, + "stderr": e.stderr + } + +@mcp.tool() +def vcftools_calculate_fst( + input_file: str, + output_prefix: str, + pop1_file: str, + pop2_file: str, + window_size: int = 0, + window_step: int = 0, +) -> dict: + """ + Calculate Weir and Cockerham Fst between two populations. + + Args: + input_file: Path to the input VCF/VCF.GZ/BCF file. + output_prefix: Prefix for the output files. + pop1_file: Path to a file containing a list of individuals in the first population. + pop2_file: Path to a file containing a list of individuals in the second population. + window_size: Size of the window for windowed Fst calculation. + window_step: Step size between windows. + """ + for f in [input_file, pop1_file, pop2_file]: + if not Path(f).exists(): + return {"error": f"File {f} not found."} + + input_flag = _get_input_flag(input_file) + cmd = [ + "vcftools", input_flag, input_file, + "--weir-fst-pop", pop1_file, + "--weir-fst-pop", pop2_file, + "--out", output_prefix + ] + + if window_size > 0: + cmd.extend(["--fst-window-size", str(window_size)]) + if window_step > 0: + cmd.extend(["--fst-window-step", str(window_step)]) + + try: + result = subprocess.run(cmd, capture_output=True, text=True, check=True) + output_files = list(Path(".").glob(f"{output_prefix}*")) + return { + "command_executed": " ".join(cmd), + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": [str(f) for f in output_files] + } + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(cmd), + "error": str(e), + "stdout": e.stdout, + "stderr": e.stderr + } + +@mcp.tool() +def vcftools_calculate_ld( + input_file: str, + output_prefix: str, + ld_type: str = "geno-r2", + ld_window_bp: int = 0, + ld_window_min_r2: float = 0.0, + min_maf: float = 0.0, +) -> dict: + """ + Calculate Linkage Disequilibrium (LD) statistics. + + Args: + input_file: Path to the input VCF/VCF.GZ/BCF file. + output_prefix: Prefix for the output files. + ld_type: Type of LD calculation. Options: 'geno-r2', 'hap-r2', 'geno-chisq'. + ld_window_bp: Maximum distance between SNPs to calculate LD. + ld_window_min_r2: Minimum r2 value to report. + min_maf: Minimum Minor Allele Frequency (recommended for LD). + """ + if not Path(input_file).exists(): + return {"error": f"Input file {input_file} not found."} + + input_flag = _get_input_flag(input_file) + cmd = ["vcftools", input_flag, input_file, "--out", output_prefix] + + ld_map = { + "geno-r2": "--geno-r2", + "hap-r2": "--hap-r2", + "geno-chisq": "--geno-chisq" + } + + if ld_type not in ld_map: + return {"error": f"Invalid ld_type. Choose from: {list(ld_map.keys())}"} + + cmd.append(ld_map[ld_type]) + + if ld_window_bp > 0: + cmd.extend(["--ld-window-bp", str(ld_window_bp)]) + if ld_window_min_r2 > 0: + cmd.extend(["--ld-window-min-r2", str(ld_window_min_r2)]) + if min_maf > 0: + cmd.extend(["--maf", str(min_maf)]) + + try: + result = subprocess.run(cmd, capture_output=True, text=True, check=True) + output_files = list(Path(".").glob(f"{output_prefix}*")) + return { + "command_executed": " ".join(cmd), + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": [str(f) for f in output_files] + } + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(cmd), + "error": str(e), + "stdout": e.stdout, + "stderr": e.stderr + } + +@mcp.tool() +def vcftools_convert_format( + input_file: str, + output_prefix: str, + target_format: str, +) -> dict: + """ + Convert VCF files to other formats like PLINK or BEAGLE. + + Args: + input_file: Path to the input VCF/VCF.GZ/BCF file. + output_prefix: Prefix for the output files. + target_format: Target format. Options: 'plink' (PED/MAP), 'plink-tped' (TPED/TFAM), 'beagle', 'impute'. + """ + if not Path(input_file).exists(): + return {"error": f"Input file {input_file} not found."} + + input_flag = _get_input_flag(input_file) + cmd = ["vcftools", input_flag, input_file, "--out", output_prefix] + + format_map = { + "plink": "--plink", + "plink-tped": "--plink-tped", + "beagle": "--beagle", + "impute": "--impute" + } + + if target_format not in format_map: + return {"error": f"Invalid target_format. Choose from: {list(format_map.keys())}"} + + cmd.append(format_map[target_format]) + + try: + result = subprocess.run(cmd, capture_output=True, text=True, check=True) + output_files = list(Path(".").glob(f"{output_prefix}*")) + return { + "command_executed": " ".join(cmd), + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": [str(f) for f in output_files] + } + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(cmd), + "error": str(e), + "stdout": e.stdout, + "stderr": e.stderr + } + +@mcp.tool() +def vcftools_subset_samples( + input_file: str, + output_prefix: str, + keep_samples_file: Optional[str] = None, + remove_samples_file: Optional[str] = None, + recode: bool = True, +) -> dict: + """ + Subset a VCF file by keeping or removing specific individuals. + + Args: + input_file: Path to the input VCF/VCF.GZ/BCF file. + output_prefix: Prefix for the output files. + keep_samples_file: Path to a file containing a list of individuals to keep. + remove_samples_file: Path to a file containing a list of individuals to remove. + recode: If True, generate a new VCF file. + """ + if not Path(input_file).exists(): + return {"error": f"Input file {input_file} not found."} + + input_flag = _get_input_flag(input_file) + cmd = ["vcftools", input_flag, input_file, "--out", output_prefix] + + if keep_samples_file: + if not Path(keep_samples_file).exists(): + return {"error": f"Keep file {keep_samples_file} not found."} + cmd.extend(["--keep", keep_samples_file]) + + if remove_samples_file: + if not Path(remove_samples_file).exists(): + return {"error": f"Remove file {remove_samples_file} not found."} + cmd.extend(["--remove", remove_samples_file]) + + if recode: + cmd.append("--recode") + cmd.append("--recode-INFO-all") + + try: + result = subprocess.run(cmd, capture_output=True, text=True, check=True) + output_files = list(Path(".").glob(f"{output_prefix}*")) + return { + "command_executed": " ".join(cmd), + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": [str(f) for f in output_files] + } + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(cmd), + "error": str(e), + "stdout": e.stdout, + "stderr": e.stderr + } \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_vcftools/app/vcftools_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_vcftools/app/vcftools_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..b88499e6ac24c10d1edcd8ff00967fe62ddd7ec3 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_vcftools/app/vcftools_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_vcftools/app/vcftools_server.py') +SERVER_NAME = 'biosci_vcftools' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_vcftools/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_vcftools/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..0730cdacd633a762fbaa2790cc7908c718900072 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_vcftools/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-vcftools: + build: . + image: mcp-vcftools:latest + container_name: mcp-vcftools + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=vcftools + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_vcftools/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_vcftools/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..d83cb9823142fa168e6398d156c54bc5c98fcb57 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_vcftools/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - vcftools + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_vcftools/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_vcftools/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_vcftools/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_viennarna/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_viennarna/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..e0fd0b42ccbc90b28cf8a2deb127bbffe76e40c3 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_viennarna/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install viennarna via conda (e.g., from bioconda) +RUN conda install -c bioconda viennarna -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY app/viennarna_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/viennarna_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/viennarna_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_viennarna/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_viennarna/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_viennarna/app/requirements.txt @@ -0,0 +1 @@ + diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_viennarna/app/viennarna_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_viennarna/app/viennarna_server.py new file mode 100644 index 0000000000000000000000000000000000000000..25fe893fc78427233ceb33472d37d10c37013fe1 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_viennarna/app/viennarna_server.py @@ -0,0 +1,729 @@ +import subprocess +from pathlib import Path +from typing import Optional, List, Dict, Any +import tempfile + +# Note: The provided documentation for 'viennarna' is a high-level overview +# and does not include detailed command-line help for its individual subcommands +# (e.g., RNAfold --help). Therefore, the parameters for each tool are generalized +# to handle common input/output patterns (input sequence/file, output file) +# and an 'extra_args' list for any specific flags not explicitly defined. +# Users should consult the official ViennaRNA documentation for specific CLI options +# and pass them via 'extra_args'. + +def _run_viennarna_tool( + tool_name: str, + input_data: Optional[str] = None, + input_file: Optional[Path] = None, + output_file: Optional[Path] = None, + extra_args: Optional[List[str]] = None, +) -> Dict[str, Any]: + """ + Helper function to execute ViennaRNA package tools. + Handles common input/output patterns and error reporting. + """ + command = [tool_name] + if extra_args: + command.extend(extra_args) + + stdin_data = None + if input_data: + if input_file: + raise ValueError(f"Cannot provide both 'input_data' and 'input_file' for {tool_name}.") + stdin_data = input_data.encode('utf-8') + elif input_file: + if not input_file.is_file(): + raise FileNotFoundError(f"Input file not found for {tool_name}: {input_file}") + # Many ViennaRNA tools can take a file path as a positional argument + # or read from stdin. We'll assume positional argument if input_file is given. + command.append(str(input_file)) + # If neither input_data nor input_file is provided, the tool might expect + # no input or specific flags via extra_args. We don't enforce input here + # as some tools might not require it (e.g., RNAparconv without args). + + output_files_generated: List[str] = [] + stdout_capture = subprocess.PIPE + stderr_capture = subprocess.PIPE + + try: + process = subprocess.run( + command, + input=stdin_data, + capture_output=True, + text=True, # Decode stdout/stderr as text + check=True + ) + stdout_content = process.stdout + stderr_content = process.stderr + + if output_file: + output_file.parent.mkdir(parents=True, exist_ok=True) + output_file.write_text(stdout_content) + output_files_generated.append(str(output_file)) + stdout_content = "" # Clear stdout if it was redirected to a file + + return { + "command_executed": " ".join(command), + "stdout": stdout_content, + "stderr": stderr_content, + "output_files": output_files_generated + } + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(command), + "stdout": e.stdout, + "stderr": e.stderr, + "error": f"Tool '{tool_name}' failed with exit code {e.returncode}: {e}", + "returncode": e.returncode, + "output_files": [] + } + except FileNotFoundError: + return { + "command_executed": " ".join(command), + "stdout": "", + "stderr": f"Error: Command '{tool_name}' not found. Is ViennaRNA installed and in PATH?", + "error": "Command not found", + "returncode": 127, + "output_files": [] + } + except Exception as e: + return { + "command_executed": " ".join(command), + "stdout": "", + "stderr": f"An unexpected error occurred: {e}", + "error": str(e), + "returncode": 1, + "output_files": [] + } + + +@mcp.tool() +def rna2dfold( + input_data: Optional[str] = None, + input_file: Optional[Path] = None, + output_file: Optional[Path] = None, + extra_args: Optional[List[str]] = None, +) -> Dict[str, Any]: + """ + Predicts RNA secondary structures including pseudoknots using a 2D folding algorithm. + + NOTE: Detailed CLI parameters are not available in the provided documentation. + This function provides generic input/output handling and an `extra_args` parameter + to pass any specific command-line options directly to the tool. + + Args: + input_data: The RNA sequence as a string. + input_file: Path to a file containing RNA sequences (e.g., FASTA format). + output_file: Optional path to write the output. If not provided, output + will be captured from stdout. + extra_args: A list of additional command-line arguments to pass to RNA2Dfold. + + Returns: + A dictionary containing the command executed, stdout, stderr, and output files. + """ + return _run_viennarna_tool("RNA2Dfold", input_data, input_file, output_file, extra_args) + + +@mcp.tool() +def rnaaliduplex( + input_data: Optional[str] = None, + input_file: Optional[Path] = None, + output_file: Optional[Path] = None, + extra_args: Optional[List[str]] = None, +) -> Dict[str, Any]: + """ + Predicts the optimal and suboptimal secondary structures of two aligned RNA sequences. + + NOTE: Detailed CLI parameters are not available in the provided documentation. + This function provides generic input/output handling and an `extra_args` parameter + to pass any specific command-line options directly to the tool. + + Args: + input_data: Two aligned RNA sequences (e.g., in FASTA format or concatenated) as a string. + input_file: Path to a file containing two aligned RNA sequences. + output_file: Optional path to write the output. If not provided, output + will be captured from stdout. + extra_args: A list of additional command-line arguments to pass to RNAaliduplex. + + Returns: + A dictionary containing the command executed, stdout, stderr, and output files. + """ + return _run_viennarna_tool("RNAaliduplex", input_data, input_file, output_file, extra_args) + + +@mcp.tool() +def rnaalifold( + input_data: Optional[str] = None, + input_file: Optional[Path] = None, + output_file: Optional[Path] = None, + extra_args: Optional[List[str]] = None, +) -> Dict[str, Any]: + """ + Predicts the consensus secondary structure and minimum free energy for a multiple + sequence alignment of RNA sequences. + + NOTE: Detailed CLI parameters are not available in the provided documentation. + This function provides generic input/output handling and an `extra_args` parameter + to pass any specific command-line options directly to the tool. + + Args: + input_data: Multiple aligned RNA sequences (e.g., in FASTA format) as a string. + input_file: Path to a file containing multiple aligned RNA sequences. + output_file: Optional path to write the output. If not provided, output + will be captured from stdout. + extra_args: A list of additional command-line arguments to pass to RNAalifold. + + Returns: + A dictionary containing the command executed, stdout, stderr, and output files. + """ + return _run_viennarna_tool("RNAalifold", input_data, input_file, output_file, extra_args) + + +@mcp.tool() +def rnacofold( + input_data: Optional[str] = None, + input_file: Optional[Path] = None, + output_file: Optional[Path] = None, + extra_args: Optional[List[str]] = None, +) -> Dict[str, Any]: + """ + Predicts the minimum free energy and partition function for the interaction + (cofolding) of two RNA sequences. + + NOTE: Detailed CLI parameters are not available in the provided documentation. + This function provides generic input/output handling and an `extra_args` parameter + to pass any specific command-line options directly to the tool. + + Args: + input_data: Two RNA sequences (e.g., separated by '&') as a string. + input_file: Path to a file containing two RNA sequences for cofolding. + output_file: Optional path to write the output. If not provided, output + will be captured from stdout. + extra_args: A list of additional command-line arguments to pass to RNAcofold. + + Returns: + A dictionary containing the command executed, stdout, stderr, and output files. + """ + return _run_viennarna_tool("RNAcofold", input_data, input_file, output_file, extra_args) + + +@mcp.tool() +def rnadistance( + input_data: Optional[str] = None, + input_file: Optional[Path] = None, + output_file: Optional[Path] = None, + extra_args: Optional[List[str]] = None, +) -> Dict[str, Any]: + """ + Calculates the distance (dissimilarity) between two RNA secondary structures. + + NOTE: Detailed CLI parameters are not available in the provided documentation. + This function provides generic input/output handling and an `extra_args` parameter + to pass any specific command-line options directly to the tool. + + Args: + input_data: Two RNA secondary structures (e.g., in dot-bracket notation) as a string. + input_file: Path to a file containing RNA secondary structures. + output_file: Optional path to write the output. If not provided, output + will be captured from stdout. + extra_args: A list of additional command-line arguments to pass to RNAdistance. + + Returns: + A dictionary containing the command executed, stdout, stderr, and output files. + """ + return _run_viennarna_tool("RNAdistance", input_data, input_file, output_file, extra_args) + + +@mcp.tool() +def rnadup_lex( + input_data: Optional[str] = None, + input_file: Optional[Path] = None, + output_file: Optional[Path] = None, + extra_args: Optional[List[str]] = None, +) -> Dict[str, Any]: + """ + Predicts the optimal and suboptimal secondary structures of two interacting RNA sequences. + + NOTE: Detailed CLI parameters are not available in the provided documentation. + This function provides generic input/output handling and an `extra_args` parameter + to pass any specific command-line options directly to the tool. + + Args: + input_data: Two RNA sequences (e.g., separated by '&') as a string. + input_file: Path to a file containing two RNA sequences for duplex prediction. + output_file: Optional path to write the output. If not provided, output + will be captured from stdout. + extra_args: A list of additional command-line arguments to pass to RNAduplex. + + Returns: + A dictionary containing the command executed, stdout, stderr, and output files. + """ + return _run_viennarna_tool("RNAduplex", input_data, input_file, output_file, extra_args) + + +@mcp.tool() +def rnaeval( + input_data: Optional[str] = None, + input_file: Optional[Path] = None, + output_file: Optional[Path] = None, + extra_args: Optional[List[str]] = None, +) -> Dict[str, Any]: + """ + Evaluates the free energy of a given RNA sequence and its secondary structure. + + NOTE: Detailed CLI parameters are not available in the provided documentation. + This function provides generic input/output handling and an `extra_args` parameter + to pass any specific command-line options directly to the tool. + + Args: + input_data: An RNA sequence and its secondary structure (e.g., "SEQUENCE\nSTRUCTURE") as a string. + input_file: Path to a file containing RNA sequences and structures. + output_file: Optional path to write the output. If not provided, output + will be captured from stdout. + extra_args: A list of additional command-line arguments to pass to RNAeval. + + Returns: + A dictionary containing the command executed, stdout, stderr, and output files. + """ + return _run_viennarna_tool("RNAeval", input_data, input_file, output_file, extra_args) + + +@mcp.tool() +def rnafold( + input_data: Optional[str] = None, + input_file: Optional[Path] = None, + output_file: Optional[Path] = None, + extra_args: Optional[List[str]] = None, +) -> Dict[str, Any]: + """ + Predicts the minimum free energy (MFE) and partition function of an RNA sequence. + + NOTE: Detailed CLI parameters are not available in the provided documentation. + This function provides generic input/output handling and an `extra_args` parameter + to pass any specific command-line options directly to the tool. + + Args: + input_data: The RNA sequence as a string. + input_file: Path to a file containing RNA sequences (e.g., FASTA format). + output_file: Optional path to write the output. If not provided, output + will be captured from stdout. + extra_args: A list of additional command-line arguments to pass to RNAfold. + + Returns: + A dictionary containing the command executed, stdout, stderr, and output files. + """ + return _run_viennarna_tool("RNAfold", input_data, input_file, output_file, extra_args) + + +@mcp.tool() +def rnaheat( + input_data: Optional[str] = None, + input_file: Optional[Path] = None, + output_file: Optional[Path] = None, + extra_args: Optional[List[str]] = None, +) -> Dict[str, Any]: + """ + Calculates the heat capacity of an RNA molecule as a function of temperature. + + NOTE: Detailed CLI parameters are not available in the provided documentation. + This function provides generic input/output handling and an `extra_args` parameter + to pass any specific command-line options directly to the tool. + + Args: + input_data: The RNA sequence as a string. + input_file: Path to a file containing RNA sequences. + output_file: Optional path to write the output. If not provided, output + will be captured from stdout. + extra_args: A list of additional command-line arguments to pass to RNAheat. + + Returns: + A dictionary containing the command executed, stdout, stderr, and output files. + """ + return _run_viennarna_tool("RNAheat", input_data, input_file, output_file, extra_args) + + +@mcp.tool() +def rnainverse( + input_data: Optional[str] = None, + input_file: Optional[Path] = None, + output_file: Optional[Path] = None, + extra_args: Optional[List[str]] = None, +) -> Dict[str, Any]: + """ + Designs an RNA sequence that folds into a predefined secondary structure (inverse folding). + + NOTE: Detailed CLI parameters are not available in the provided documentation. + This function provides generic input/output handling and an `extra_args` parameter + to pass any specific command-line options directly to the tool. + + Args: + input_data: The target RNA secondary structure (e.g., in dot-bracket notation) as a string. + input_file: Path to a file containing target RNA secondary structures. + output_file: Optional path to write the output. If not provided, output + will be captured from stdout. + extra_args: A list of additional command-line arguments to pass to RNAinverse. + + Returns: + A dictionary containing the command executed, stdout, stderr, and output files. + """ + return _run_viennarna_tool("RNAinverse", input_data, input_file, output_file, extra_args) + + +@mcp.tool() +def rnalalifold( + input_data: Optional[str] = None, + input_file: Optional[Path] = None, + output_file: Optional[Path] = None, + extra_args: Optional[List[str]] = None, +) -> Dict[str, Any]: + """ + Predicts local consensus secondary structures for a multiple sequence alignment. + + NOTE: Detailed CLI parameters are not available in the provided documentation. + This function provides generic input/output handling and an `extra_args` parameter + to pass any specific command-line options directly to the tool. + + Args: + input_data: Multiple aligned RNA sequences (e.g., in FASTA format) as a string. + input_file: Path to a file containing multiple aligned RNA sequences. + output_file: Optional path to write the output. If not provided, output + will be captured from stdout. + extra_args: A list of additional command-line arguments to pass to RNALalifold. + + Returns: + A dictionary containing the command executed, stdout, stderr, and output files. + """ + return _run_viennarna_tool("RNALalifold", input_data, input_file, output_file, extra_args) + + +@mcp.tool() +def rnalfold( + input_data: Optional[str] = None, + input_file: Optional[Path] = None, + output_file: Optional[Path] = None, + extra_args: Optional[List[str]] = None, +) -> Dict[str, Any]: + """ + Predicts local secondary structures for a long RNA sequence using a sliding window approach. + + NOTE: Detailed CLI parameters are not available in the provided documentation. + This function provides generic input/output handling and an `extra_args` parameter + to pass any specific command-line options directly to the tool. + + Args: + input_data: The RNA sequence as a string. + input_file: Path to a file containing RNA sequences (e.g., FASTA format). + output_file: Optional path to write the output. If not provided, output + will be captured from stdout. + extra_args: A list of additional command-line arguments to pass to RNALfold. + + Returns: + A dictionary containing the command executed, stdout, stderr, and output files. + """ + return _run_viennarna_tool("RNALfold", input_data, input_file, output_file, extra_args) + + +@mcp.tool() +def rnapaln( + input_data: Optional[str] = None, + input_file: Optional[Path] = None, + output_file: Optional[Path] = None, + extra_args: Optional[List[str]] = None, +) -> Dict[str, Any]: + """ + Performs pairwise alignment of RNA sequences, considering both sequence and structure. + + NOTE: Detailed CLI parameters are not available in the provided documentation. + This function provides generic input/output handling and an `extra_args` parameter + to pass any specific command-line options directly to the tool. + + Args: + input_data: Two RNA sequences (e.g., in FASTA format or concatenated) as a string. + input_file: Path to a file containing two RNA sequences. + output_file: Optional path to write the output. If not provided, output + will be captured from stdout. + extra_args: A list of additional command-line arguments to pass to RNApaln. + + Returns: + A dictionary containing the command executed, stdout, stderr, and output files. + """ + return _run_viennarna_tool("RNApaln", input_data, input_file, output_file, extra_args) + + +@mcp.tool() +def rnapdist( + input_data: Optional[str] = None, + input_file: Optional[Path] = None, + output_file: Optional[Path] = None, + extra_args: Optional[List[str]] = None, +) -> Dict[str, Any]: + """ + Calculates pairwise distances between multiple RNA secondary structures. + + NOTE: Detailed CLI parameters are not available in the provided documentation. + This function provides generic input/output handling and an `extra_args` parameter + to pass any specific command-line options directly to the tool. + + Args: + input_data: Multiple RNA secondary structures (e.g., in dot-bracket notation) as a string. + input_file: Path to a file containing multiple RNA secondary structures. + output_file: Optional path to write the output. If not provided, output + will be captured from stdout. + extra_args: A list of additional command-line arguments to pass to RNApdist. + + Returns: + A dictionary containing the command executed, stdout, stderr, and output files. + """ + return _run_viennarna_tool("RNApdist", input_data, input_file, output_file, extra_args) + + +@mcp.tool() +def rna_parconv( + input_data: Optional[str] = None, + input_file: Optional[Path] = None, + output_file: Optional[Path] = None, + extra_args: Optional[List[str]] = None, +) -> Dict[str, Any]: + """ + Converts RNA secondary structure parameters between different formats or versions. + + NOTE: Detailed CLI parameters are not available in the provided documentation. + This function provides generic input/output handling and an `extra_args` parameter + to pass any specific command-line options directly to the tool. + + Args: + input_data: Input parameters as a string (format depends on tool usage). + input_file: Path to a file containing input parameters. + output_file: Optional path to write the output. If not provided, output + will be captured from stdout. + extra_args: A list of additional command-line arguments to pass to RNAparconv. + + Returns: + A dictionary containing the command executed, stdout, stderr, and output files. + """ + return _run_viennarna_tool("RNAparconv", input_data, input_file, output_file, extra_args) + + +@mcp.tool() +def rnap_kplex( + input_data: Optional[str] = None, + input_file: Optional[Path] = None, + output_file: Optional[Path] = None, + extra_args: Optional[List[str]] = None, +) -> Dict[str, Any]: + """ + Predicts RNA secondary structures including pseudoknots using a heuristic approach. + + NOTE: Detailed CLI parameters are not available in the provided documentation. + This function provides generic input/output handling and an `extra_args` parameter + to pass any specific command-line options directly to the tool. + + Args: + input_data: The RNA sequence as a string. + input_file: Path to a file containing RNA sequences. + output_file: Optional path to write the output. If not provided, output + will be captured from stdout. + extra_args: A list of additional command-line arguments to pass to RNAPKplex. + + Returns: + A dictionary containing the command executed, stdout, stderr, and output files. + """ + return _run_viennarna_tool("RNAPKplex", input_data, input_file, output_file, extra_args) + + +@mcp.tool() +def rnaplex( + input_data: Optional[str] = None, + input_file: Optional[Path] = None, + output_file: Optional[Path] = None, + extra_args: Optional[List[str]] = None, +) -> Dict[str, Any]: + """ + Predicts RNA-RNA interaction sites and binding energies. + + NOTE: Detailed CLI parameters are not available in the provided documentation. + This function provides generic input/output handling and an `extra_args` parameter + to pass any specific command-line options directly to the tool. + + Args: + input_data: Two RNA sequences (e.g., separated by '&') as a string. + input_file: Path to a file containing two RNA sequences for interaction prediction. + output_file: Optional path to write the output. If not provided, output + will be captured from stdout. + extra_args: A list of additional command-line arguments to pass to RNAplex. + + Returns: + A dictionary containing the command executed, stdout, stderr, and output files. + """ + return _run_viennarna_tool("RNAplex", input_data, input_file, output_file, extra_args) + + +@mcp.tool() +def rnaplfold( + input_data: Optional[str] = None, + input_file: Optional[Path] = None, + output_file: Optional[Path] = None, + extra_args: Optional[List[str]] = None, +) -> Dict[str, Any]: + """ + Calculates base pair probabilities and local secondary structures for long RNA sequences. + + NOTE: Detailed CLI parameters are not available in the provided documentation. + This function provides generic input/output handling and an `extra_args` parameter + to pass any specific command-line options directly to the tool. + + Args: + input_data: The RNA sequence as a string. + input_file: Path to a file containing RNA sequences (e.g., FASTA format). + output_file: Optional path to write the output. If not provided, output + will be captured from stdout. + extra_args: A list of additional command-line arguments to pass to RNAplfold. + + Returns: + A dictionary containing the command executed, stdout, stderr, and output files. + """ + return _run_viennarna_tool("RNAplfold", input_data, input_file, output_file, extra_args) + + +@mcp.tool() +def rnaplot( + input_data: Optional[str] = None, + input_file: Optional[Path] = None, + output_file: Optional[Path] = None, + extra_args: Optional[List[str]] = None, +) -> Dict[str, Any]: + """ + Generates graphical representations of RNA secondary structures. + Typically outputs PostScript or SVG files. + + NOTE: Detailed CLI parameters are not available in the provided documentation. + This function provides generic input/output handling. For graphical output, + you will likely need to specify the output file format and name using `extra_args` + (e.g., `extra_args=["-o", "output.ps"]`). If `output_file` is provided, + this function will attempt to write stdout to it, which might not be the + desired behavior for binary graphical output. + + Args: + input_data: An RNA sequence and its secondary structure (e.g., "SEQUENCE\nSTRUCTURE") as a string. + input_file: Path to a file containing RNA sequences and structures. + output_file: Optional path to write the output. If not provided, output + will be captured from stdout. For graphical output, consider + using `extra_args` to specify the output file directly to RNAplot. + extra_args: A list of additional command-line arguments to pass to RNAplot. + + Returns: + A dictionary containing the command executed, stdout, stderr, and output files. + """ + # RNAplot often writes directly to a file specified by an argument (e.g., -o). + # If output_file is provided here, we'll still capture stdout and write it, + # but the user should ideally use extra_args to control RNAplot's file output. + return _run_viennarna_tool("RNAplot", input_data, input_file, output_file, extra_args) + + +@mcp.tool() +def rnasnoop( + input_data: Optional[str] = None, + input_file: Optional[Path] = None, + output_file: Optional[Path] = None, + extra_args: Optional[List[str]] = None, +) -> Dict[str, Any]: + """ + Predicts snoRNA (small nucleolar RNA) targets and interactions. + + NOTE: Detailed CLI parameters are not available in the provided documentation. + This function provides generic input/output handling and an `extra_args` parameter + to pass any specific command-line options directly to the tool. + + Args: + input_data: RNA sequences (e.g., snoRNA and target) as a string. + input_file: Path to a file containing RNA sequences. + output_file: Optional path to write the output. If not provided, output + will be captured from stdout. + extra_args: A list of additional command-line arguments to pass to RNAsnoop. + + Returns: + A dictionary containing the command executed, stdout, stderr, and output files. + """ + return _run_viennarna_tool("RNAsnoop", input_data, input_file, output_file, extra_args) + + +@mcp.tool() +def rnasubopt( + input_data: Optional[str] = None, + input_file: Optional[Path] = None, + output_file: Optional[Path] = None, + extra_args: Optional[List[str]] = None, +) -> Dict[str, Any]: + """ + Generates suboptimal RNA secondary structures within a given energy range of the MFE. + + NOTE: Detailed CLI parameters are not available in the provided documentation. + This function provides generic input/output handling and an `extra_args` parameter + to pass any specific command-line options directly to the tool. + + Args: + input_data: The RNA sequence as a string. + input_file: Path to a file containing RNA sequences (e.g., FASTA format). + output_file: Optional path to write the output. If not provided, output + will be captured from stdout. + extra_args: A list of additional command-line arguments to pass to RNAsubopt. + + Returns: + A dictionary containing the command executed, stdout, stderr, and output files. + """ + return _run_viennarna_tool("RNAsubopt", input_data, input_file, output_file, extra_args) + + +@mcp.tool() +def rnaup( + input_data: Optional[str] = None, + input_file: Optional[Path] = None, + output_file: Optional[Path] = None, + extra_args: Optional[List[str]] = None, +) -> Dict[str, Any]: + """ + Predicts the accessibility of RNA regions for interaction with other molecules. + + NOTE: Detailed CLI parameters are not available in the provided documentation. + This function provides generic input/output handling and an `extra_args` parameter + to pass any specific command-line options directly to the tool. + + Args: + input_data: The RNA sequence as a string. + input_file: Path to a file containing RNA sequences. + output_file: Optional path to write the output. If not provided, output + will be captured from stdout. + extra_args: A list of additional command-line arguments to pass to RNAup. + + Returns: + A dictionary containing the command executed, stdout, stderr, and output files. + """ + return _run_viennarna_tool("RNAup", input_data, input_file, output_file, extra_args) + + +@mcp.tool() +def rnamultifold( + input_data: Optional[str] = None, + input_file: Optional[Path] = None, + output_file: Optional[Path] = None, + extra_args: Optional[List[str]] = None, +) -> Dict[str, Any]: + """ + Predicts multi-strand RNA complex formation and equilibrium concentrations. + This tool is a successor to RNAcofold, lifting the restriction to just two interacting strands. + + NOTE: Detailed CLI parameters are not available in the provided documentation. + This function provides generic input/output handling and an `extra_args` parameter + to pass any specific command-line options directly to the tool. + + Args: + input_data: Multiple RNA sequences (e.g., separated by '&') as a string. + input_file: Path to a file containing multiple RNA sequences for multi-strand folding. + output_file: Optional path to write the output. If not provided, output + will be captured from stdout. + extra_args: A list of additional command-line arguments to pass to RNAmultifold. + + Returns: + A dictionary containing the command executed, stdout, stderr, and output files. + """ + return _run_viennarna_tool("RNAmultifold", input_data, input_file, output_file, extra_args) \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_viennarna/app/viennarna_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_viennarna/app/viennarna_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..ba37b01ecbb9892cf415c0de6dbc241498c7717c --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_viennarna/app/viennarna_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_viennarna/app/viennarna_server.py') +SERVER_NAME = 'biosci_viennarna' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_viennarna/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_viennarna/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..258027c499b42f86cf384a6a0c178d6eb4583de2 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_viennarna/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-viennarna: + build: . + image: mcp-viennarna:latest + container_name: mcp-viennarna + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=viennarna + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_viennarna/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_viennarna/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..d4a78ff2436f610b96df226f957b176de2559ab4 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_viennarna/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - viennarna + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_viennarna/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_viennarna/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_viennarna/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_whatshap/Dockerfile b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_whatshap/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..fe2aea8f56b4da753d6b3e6613798bebe6f2c12f --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_whatshap/Dockerfile @@ -0,0 +1,40 @@ + +FROM python:3.10-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y default-jre wget curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Miniconda +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 + +# Add conda to PATH +ENV PATH="/opt/conda/bin:$PATH" + +# Install whatshap via conda (e.g., from bioconda) +RUN conda install -c bioconda whatshap -y && conda clean -a + +# Install Python dependencies +RUN pip install uv +RUN uv pip install --system fastmcp + +# Create app directory +WORKDIR /app + +# Copy your MCP server +COPY app/whatshap_server.py /app/ + +# Create workspace and output directories +RUN mkdir -p /app/workspace /app/output + +# Make sure the server script is executable +RUN chmod +x /app/whatshap_server.py + +# Expose port for MCP over HTTP (optional) +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD python -c "import sys; sys.exit(0)" + +# Default command runs the MCP server via stdio +CMD ["python", "/app/whatshap_server.py"] + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_whatshap/app/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_whatshap/app/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_whatshap/app/requirements.txt @@ -0,0 +1 @@ + diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_whatshap/app/whatshap_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_whatshap/app/whatshap_server.py new file mode 100644 index 0000000000000000000000000000000000000000..89db2be85f97a750a0743281ae86861ac7a66210 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_whatshap/app/whatshap_server.py @@ -0,0 +1,450 @@ +import subprocess +from pathlib import Path +from typing import List, Optional, Union + +@mcp.tool() +def whatshap_phase( + vcf: str, + phase_input: List[str], + reference: Optional[str] = None, + output: Optional[str] = None, + samples: Optional[List[str]] = None, + chromosomes: Optional[List[str]] = None, + ignore_read_groups: bool = False, + indels: bool = True, + distrust_genotypes: bool = False, + ignore_pedigree: bool = False, + mapq: int = 20, + max_coverage: int = 15, + tag: str = "PS", +): + """ + Phase genomic variants using DNA sequencing reads. + + Args: + vcf: Path to the input VCF file. + phase_input: List of paths to BAM/CRAM files. + reference: Path to the reference FASTA file. + output: Path to the output phased VCF file. + samples: List of sample names to phase. + chromosomes: List of chromosomes to phase. + ignore_read_groups: Ignore read groups in BAM/CRAM files. + indels: Also phase indels (default: True). + distrust_genotypes: Distrust genotypes in the input VCF. + ignore_pedigree: Ignore pedigree information in VCF. + mapq: Minimum mapping quality (default: 20). + max_coverage: Maximum coverage per sample (default: 15). + tag: VCF tag to use for phasing information (default: PS). + """ + vcf_path = Path(vcf) + if not vcf_path.exists(): + return {"error": f"VCF file not found: {vcf}"} + + cmd = ["whatshap", "phase", "--mapq", str(mapq), "--max-coverage", str(max_coverage), "--tag", tag] + + if reference: + ref_path = Path(reference) + if not ref_path.exists(): + return {"error": f"Reference file not found: {reference}"} + cmd.extend(["--reference", reference]) + + if output: + cmd.extend(["--output", output]) + + if samples: + cmd.extend(["--sample", ",".join(samples)]) + + if chromosomes: + cmd.extend(["--chromosome", ",".join(chromosomes)]) + + if ignore_read_groups: + cmd.append("--ignore-read-groups") + + if not indels: + cmd.append("--no-indels") + + if distrust_genotypes: + cmd.append("--distrust-genotypes") + + if ignore_pedigree: + cmd.append("--ignore-pedigree") + + cmd.append(vcf) + cmd.extend(phase_input) + + try: + result = subprocess.run(cmd, capture_output=True, text=True, check=True) + return { + "command_executed": " ".join(cmd), + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": [output] if output else [] + } + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(cmd), + "error": str(e), + "stdout": e.stdout, + "stderr": e.stderr + } + +@mcp.tool() +def whatshap_stats( + vcf: str, + gtf: Optional[str] = None, + tsv: Optional[str] = None, + block_list: Optional[str] = None, + sample: Optional[str] = None, + chromosome: Optional[str] = None, +): + """ + Calculate phasing statistics for a phased VCF file. + + Args: + vcf: Path to the phased VCF file. + gtf: Write phasing blocks to GTF file. + tsv: Write statistics to TSV file. + block_list: Write block list to this file. + sample: Sample name to process. + chromosome: Chromosome to process. + """ + if not Path(vcf).exists(): + return {"error": f"VCF file not found: {vcf}"} + + cmd = ["whatshap", "stats"] + if gtf: + cmd.extend(["--gtf", gtf]) + if tsv: + cmd.extend(["--tsv", tsv]) + if block_list: + cmd.extend(["--block-list", block_list]) + if sample: + cmd.extend(["--sample", sample]) + if chromosome: + cmd.extend(["--chromosome", chromosome]) + + cmd.append(vcf) + + try: + result = subprocess.run(cmd, capture_output=True, text=True, check=True) + return { + "command_executed": " ".join(cmd), + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": [f for f in [gtf, tsv, block_list] if f] + } + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(cmd), + "error": str(e), + "stdout": e.stdout, + "stderr": e.stderr + } + +@mcp.tool() +def whatshap_compare( + vcf1: str, + vcf2: str, + sample: Optional[str] = None, + names: Optional[str] = None, + tsv_pairwise: Optional[str] = None, + longest_block_tsv: Optional[str] = None, + switch_error_bed: Optional[str] = None, +): + """ + Compare two phasings (e.g., compare assembly to ground truth). + + Args: + vcf1: First phased VCF file. + vcf2: Second phased VCF file. + sample: Sample name to compare. + names: Comma-separated names for the two VCFs in output. + tsv_pairwise: Write pairwise comparison to TSV. + longest_block_tsv: Write longest block statistics to TSV. + switch_error_bed: Write switch errors to BED file. + """ + if not Path(vcf1).exists() or not Path(vcf2).exists(): + return {"error": "One or both VCF files not found."} + + cmd = ["whatshap", "compare"] + if sample: + cmd.extend(["--sample", sample]) + if names: + cmd.extend(["--names", names]) + if tsv_pairwise: + cmd.extend(["--tsv-pairwise", tsv_pairwise]) + if longest_block_tsv: + cmd.extend(["--longest-block-tsv", longest_block_tsv]) + if switch_error_bed: + cmd.extend(["--switch-error-bed", switch_error_bed]) + + cmd.extend([vcf1, vcf2]) + + try: + result = subprocess.run(cmd, capture_output=True, text=True, check=True) + return { + "command_executed": " ".join(cmd), + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": [f for f in [tsv_pairwise, longest_block_tsv, switch_error_bed] if f] + } + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(cmd), + "error": str(e), + "stdout": e.stdout, + "stderr": e.stderr + } + +@mcp.tool() +def whatshap_haplotag( + vcf: str, + bam: str, + reference: Optional[str] = None, + output: Optional[str] = None, + sample: Optional[str] = None, + ignore_read_groups: bool = False, + regions: Optional[str] = None, + tag_supplementary: bool = False, +): + """ + Tag reads in a BAM file with haplotype information (HP and PS tags). + + Args: + vcf: Path to the phased VCF file. + bam: Path to the BAM/CRAM file. + reference: Path to the reference FASTA file. + output: Path to the output tagged BAM file. + sample: Sample name to process. + ignore_read_groups: Ignore read groups. + regions: Only process these regions (e.g. chr1:100-200). + tag_supplementary: Tag supplementary alignments. + """ + if not Path(vcf).exists() or not Path(bam).exists(): + return {"error": "VCF or BAM file not found."} + + cmd = ["whatshap", "haplotag"] + if reference: + cmd.extend(["--reference", reference]) + if output: + cmd.extend(["--output", output]) + if sample: + cmd.extend(["--sample", sample]) + if ignore_read_groups: + cmd.append("--ignore-read-groups") + if regions: + cmd.extend(["--regions", regions]) + if tag_supplementary: + cmd.append("--tag-supplementary") + + cmd.extend([vcf, bam]) + + try: + result = subprocess.run(cmd, capture_output=True, text=True, check=True) + return { + "command_executed": " ".join(cmd), + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": [output] if output else [] + } + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(cmd), + "error": str(e), + "stdout": e.stdout, + "stderr": e.stderr + } + +@mcp.tool() +def whatshap_polyphase( + vcf: str, + phase_input: List[str], + ploidy: int = 3, + reference: Optional[str] = None, + output: Optional[str] = None, + samples: Optional[List[str]] = None, + chromosomes: Optional[List[str]] = None, + mapq: int = 20, +): + """ + Phase genomic variants for polyploid organisms. + + Args: + vcf: Path to the input VCF file. + phase_input: List of paths to BAM/CRAM files. + ploidy: Ploidy of the organism (default: 3). + reference: Path to the reference FASTA file. + output: Path to the output phased VCF file. + samples: List of sample names to phase. + chromosomes: List of chromosomes to phase. + mapq: Minimum mapping quality (default: 20). + """ + if ploidy < 2: + return {"error": "Ploidy must be at least 2."} + + cmd = ["whatshap", "polyphase", "--ploidy", str(ploidy), "--mapq", str(mapq)] + + if reference: + cmd.extend(["--reference", reference]) + if output: + cmd.extend(["--output", output]) + if samples: + cmd.extend(["--sample", ",".join(samples)]) + if chromosomes: + cmd.extend(["--chromosome", ",".join(chromosomes)]) + + cmd.append(vcf) + cmd.extend(phase_input) + + try: + result = subprocess.run(cmd, capture_output=True, text=True, check=True) + return { + "command_executed": " ".join(cmd), + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": [output] if output else [] + } + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(cmd), + "error": str(e), + "stdout": e.stdout, + "stderr": e.stderr + } + +@mcp.tool() +def whatshap_split( + bam: str, + haplist: str, + output_h1: str, + output_h2: str, + output_untagged: Optional[str] = None, +): + """ + Split a BAM file into two files, one for each haplotype. + + Args: + bam: Path to the input BAM file. + haplist: Path to the haplotype list (from haplotag). + output_h1: Path to output BAM for haplotype 1. + output_h2: Path to output BAM for haplotype 2. + output_untagged: Path to output BAM for untagged reads. + """ + if not Path(bam).exists() or not Path(haplist).exists(): + return {"error": "BAM or haplist file not found."} + + cmd = ["whatshap", "split", "--output-h1", output_h1, "--output-h2", output_h2] + if output_untagged: + cmd.extend(["--output-untagged", output_untagged]) + + cmd.extend([bam, haplist]) + + try: + result = subprocess.run(cmd, capture_output=True, text=True, check=True) + return { + "command_executed": " ".join(cmd), + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": [output_h1, output_h2] + ([output_untagged] if output_untagged else []) + } + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(cmd), + "error": str(e), + "stdout": e.stdout, + "stderr": e.stderr + } + +@mcp.tool() +def whatshap_genotype( + vcf: str, + phase_input: List[str], + reference: str, + output: Optional[str] = None, + sample: Optional[str] = None, + chromosomes: Optional[List[str]] = None, + mapq: int = 20, + indels: bool = True, +): + """ + Genotype variants using DNA sequencing reads. + + Args: + vcf: Path to the input VCF file. + phase_input: List of paths to BAM/CRAM files. + reference: Path to the reference FASTA file (required). + output: Path to the output VCF file. + sample: Sample name to genotype. + chromosomes: List of chromosomes to genotype. + mapq: Minimum mapping quality (default: 20). + indels: Genotype indels (default: True). + """ + if not Path(reference).exists(): + return {"error": f"Reference file not found: {reference}"} + + cmd = ["whatshap", "genotype", "--reference", reference, "--mapq", str(mapq)] + + if output: + cmd.extend(["--output", output]) + if sample: + cmd.extend(["--sample", sample]) + if chromosomes: + cmd.extend(["--chromosome", ",".join(chromosomes)]) + if not indels: + cmd.append("--no-indels") + + cmd.append(vcf) + cmd.extend(phase_input) + + try: + result = subprocess.run(cmd, capture_output=True, text=True, check=True) + return { + "command_executed": " ".join(cmd), + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": [output] if output else [] + } + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(cmd), + "error": str(e), + "stdout": e.stdout, + "stderr": e.stderr + } + +@mcp.tool() +def whatshap_unphase( + vcf: str, + output: Optional[str] = None, +): + """ + Remove phasing information from a VCF file. + + Args: + vcf: Path to the input VCF file. + output: Path to the output unphased VCF file. + """ + if not Path(vcf).exists(): + return {"error": f"VCF file not found: {vcf}"} + + cmd = ["whatshap", "unphase"] + if output: + cmd.extend(["--output", output]) + + cmd.append(vcf) + + try: + result = subprocess.run(cmd, capture_output=True, text=True, check=True) + return { + "command_executed": " ".join(cmd), + "stdout": result.stdout, + "stderr": result.stderr, + "output_files": [output] if output else [] + } + except subprocess.CalledProcessError as e: + return { + "command_executed": " ".join(cmd), + "error": str(e), + "stdout": e.stdout, + "stderr": e.stderr + } \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_whatshap/app/whatshap_shim_server.py b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_whatshap/app/whatshap_shim_server.py new file mode 100644 index 0000000000000000000000000000000000000000..f0b4d016dde46474b17ddd72a9a11933b17377a8 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_whatshap/app/whatshap_shim_server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import ast +from pathlib import Path + +from mcp.server.fastmcp import FastMCP + + +SOURCE_SERVER = Path('/225040511/project/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_whatshap/app/whatshap_server.py') +SERVER_NAME = 'biosci_whatshap' + + +class _ShimMCP: + @staticmethod + def tool(): + def _decorator(fn): + return fn + return _decorator + + +def _load_functions(): + code = SOURCE_SERVER.read_text(encoding="utf-8") + tree = ast.parse(code, filename=str(SOURCE_SERVER)) + function_names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef) and not n.name.startswith("_")] + namespace = { + "__name__": "__mcp_source__", + "mcp": _ShimMCP(), + } + exec(compile(code, str(SOURCE_SERVER), "exec"), namespace, namespace) + loaded = [] + for name in function_names: + fn = namespace.get(name) + if callable(fn): + loaded.append(fn) + return loaded + + +mcp = FastMCP(SERVER_NAME) +for _fn in _load_functions(): + mcp.tool()(_fn) + + +if __name__ == "__main__": + mcp.run(transport="stdio") diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_whatshap/docker-compose.yml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_whatshap/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..7fbe463550c59f48c02c9a334cffc2c588ad2e42 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_whatshap/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + mcp-whatshap: + build: . + image: mcp-whatshap:latest + container_name: mcp-whatshap + ports: + - "8000:8000" + environment: + - MCP_SERVER_NAME=whatshap + volumes: + - ./workspace:/app/workspace + - ./output:/app/output + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_whatshap/environment.yaml b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_whatshap/environment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..ba68847dbc709bd65e4df8721efa65b6625c0a9d --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_whatshap/environment.yaml @@ -0,0 +1,10 @@ + +name: mcp-tool +channels: + - bioconda + - conda-forge + - defaults +dependencies: + - whatshap + - python=3.10 + \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_whatshap/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_whatshap/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_whatshap/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_xopen/requirements.txt b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_xopen/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc4702c254694b94825ae9800a56d8f68b1bcda0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/mcp_batch_from_manual_txt/mcp_xopen/requirements.txt @@ -0,0 +1,2 @@ +fastmcp +mcp diff --git a/BioScientist/agent_system/toolbase/output/help_docs/help_txt/abyss.help.txt b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/abyss.help.txt new file mode 100644 index 0000000000000000000000000000000000000000..755e9fb3160938b710cc6d5fd140fd6106203e93 --- /dev/null +++ b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/abyss.help.txt @@ -0,0 +1,36 @@ +$ conda run -n bioenv perl -h +[rc=0] + +Usage: perl [switches] [--] [programfile] [arguments] + -0[octal] specify record separator (\0, if no argument) + -a autosplit mode with -n or -p (splits $_ into @F) + -C[number/list] enables the listed Unicode features + -c check syntax only (runs BEGIN and CHECK blocks) + -d[:debugger] run program under debugger + -D[number/list] set debugging flags (argument is a bit mask or alphabets) + -e program one line of program (several -e's allowed, omit programfile) + -E program like -e, but enables all optional features + -f don't do $sitelib/sitecustomize.pl at startup + -F/pattern/ split() pattern for -a switch (//'s are optional) + -i[extension] edit <> files in place (makes backup if extension supplied) + -Idirectory specify @INC/#include directory (several -I's allowed) + -l[octal] enable line ending processing, specifies line terminator + -[mM][-]module execute "use/no module..." before executing program + -n assume "while (<>) { ... }" loop around program + -p assume loop like -n but print line also, like sed + -s enable rudimentary parsing for switches after programfile + -S look for programfile using PATH environment variable + -t enable tainting warnings + -T enable tainting checks + -u dump core after parsing program + -U allow unsafe operations + -v print version, patchlevel and license + -V[:variable] print configuration summary (or a single Config.pm variable) + -w enable many useful warnings + -W enable all warnings + -x[directory] ignore text before #!perl line (optionally cd to directory) + -X disable all warnings + +Run 'perldoc perl' for more help with Perl. + + diff --git a/BioScientist/agent_system/toolbase/output/help_docs/help_txt/bioconductor-biocparallel.help.txt b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/bioconductor-biocparallel.help.txt new file mode 100644 index 0000000000000000000000000000000000000000..ba8c3c1d7d1e908b1f302a0542761a1346b180c8 --- /dev/null +++ b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/bioconductor-biocparallel.help.txt @@ -0,0 +1,25 @@ +$ conda run -n bioenv Rscript --help +[rc=0] + +Usage: /path/to/Rscript [--options] [-e expr [-e expr2 ...] | file] [args] + +--options accepted are + --help Print usage and exit + --version Print version and exit + --verbose Print information on progress + --default-packages=list + Where 'list' is a comma-separated set + of package names, or 'NULL' +or options to R, in addition to --no-echo --no-restore, such as + --save Do save workspace at the end of the session + --no-environ Don't read the site and user environment files + --no-site-file Don't read the site-wide Rprofile + --no-init-file Don't read the user R profile + --restore Do restore previously saved objects at startup + --vanilla Combine --no-save, --no-restore, --no-site-file + --no-init-file and --no-environ + +'file' may contain spaces but not shell metacharacters +Expressions (one or more '-e ') may be used *instead* of 'file' +See also ?Rscript from within R + diff --git a/BioScientist/agent_system/toolbase/output/help_docs/help_txt/bioconductor-bsgenome.help.txt b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/bioconductor-bsgenome.help.txt new file mode 100644 index 0000000000000000000000000000000000000000..9396ee6dd4e0ed35a6252ddcf0e196deafac3795 --- /dev/null +++ b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/bioconductor-bsgenome.help.txt @@ -0,0 +1,6 @@ +$ conda run -n bioenv_r_bioc Rscript --help +[rc=127] + +Rscript: error while loading shared libraries: libgfortran.so.3: cannot open shared object file: No such file or directory + +ERROR conda.cli.main_run:execute(127): `conda run Rscript --help` failed. (See above for error) diff --git a/BioScientist/agent_system/toolbase/output/help_docs/help_txt/bioconductor-dnacopy.help.txt b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/bioconductor-dnacopy.help.txt new file mode 100644 index 0000000000000000000000000000000000000000..9396ee6dd4e0ed35a6252ddcf0e196deafac3795 --- /dev/null +++ b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/bioconductor-dnacopy.help.txt @@ -0,0 +1,6 @@ +$ conda run -n bioenv_r_bioc Rscript --help +[rc=127] + +Rscript: error while loading shared libraries: libgfortran.so.3: cannot open shared object file: No such file or directory + +ERROR conda.cli.main_run:execute(127): `conda run Rscript --help` failed. (See above for error) diff --git a/BioScientist/agent_system/toolbase/output/help_docs/help_txt/bioconductor-geneplotter.help.txt b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/bioconductor-geneplotter.help.txt new file mode 100644 index 0000000000000000000000000000000000000000..9396ee6dd4e0ed35a6252ddcf0e196deafac3795 --- /dev/null +++ b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/bioconductor-geneplotter.help.txt @@ -0,0 +1,6 @@ +$ conda run -n bioenv_r_bioc Rscript --help +[rc=127] + +Rscript: error while loading shared libraries: libgfortran.so.3: cannot open shared object file: No such file or directory + +ERROR conda.cli.main_run:execute(127): `conda run Rscript --help` failed. (See above for error) diff --git a/BioScientist/agent_system/toolbase/output/help_docs/help_txt/bioconductor-genomicalignments.help.txt b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/bioconductor-genomicalignments.help.txt new file mode 100644 index 0000000000000000000000000000000000000000..9396ee6dd4e0ed35a6252ddcf0e196deafac3795 --- /dev/null +++ b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/bioconductor-genomicalignments.help.txt @@ -0,0 +1,6 @@ +$ conda run -n bioenv_r_bioc Rscript --help +[rc=127] + +Rscript: error while loading shared libraries: libgfortran.so.3: cannot open shared object file: No such file or directory + +ERROR conda.cli.main_run:execute(127): `conda run Rscript --help` failed. (See above for error) diff --git a/BioScientist/agent_system/toolbase/output/help_docs/help_txt/bioconductor-iranges.help.txt b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/bioconductor-iranges.help.txt new file mode 100644 index 0000000000000000000000000000000000000000..71801163bb9849e76c8ca06ae9f913c24be7df84 --- /dev/null +++ b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/bioconductor-iranges.help.txt @@ -0,0 +1,25 @@ +$ conda run -n bioenv_r_bioc Rscript --help +[rc=0] + +Usage: /path/to/Rscript [--options] [-e expr [-e expr2 ...] | file] [args] + +--options accepted are + --help Print usage and exit + --version Print version and exit + --verbose Print information on progress + --default-packages=list + Where 'list' is a comma-separated set + of package names, or 'NULL' +or options to R, in addition to --no-echo --no-restore, such as + --save Do save workspace at the end of the session + --no-environ Don't read the site and user environment files + --no-site-file Don't read the site-wide Rprofile + --no-init-file Don't read the user R profile + --restore Do restore previously saved objects at startup + --vanilla Combine --no-save, --no-restore, --no-site-file + --no-init-file and --no-environ + +'file' may contain spaces but not shell metacharacters +Expressions (one or more '-e ') may be used *instead* of 'file' +See also ?Rscript from within R + diff --git a/BioScientist/agent_system/toolbase/output/help_docs/help_txt/bioconductor-mosim.help.txt b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/bioconductor-mosim.help.txt new file mode 100644 index 0000000000000000000000000000000000000000..ba8c3c1d7d1e908b1f302a0542761a1346b180c8 --- /dev/null +++ b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/bioconductor-mosim.help.txt @@ -0,0 +1,25 @@ +$ conda run -n bioenv Rscript --help +[rc=0] + +Usage: /path/to/Rscript [--options] [-e expr [-e expr2 ...] | file] [args] + +--options accepted are + --help Print usage and exit + --version Print version and exit + --verbose Print information on progress + --default-packages=list + Where 'list' is a comma-separated set + of package names, or 'NULL' +or options to R, in addition to --no-echo --no-restore, such as + --save Do save workspace at the end of the session + --no-environ Don't read the site and user environment files + --no-site-file Don't read the site-wide Rprofile + --no-init-file Don't read the user R profile + --restore Do restore previously saved objects at startup + --vanilla Combine --no-save, --no-restore, --no-site-file + --no-init-file and --no-environ + +'file' may contain spaces but not shell metacharacters +Expressions (one or more '-e ') may be used *instead* of 'file' +See also ?Rscript from within R + diff --git a/BioScientist/agent_system/toolbase/output/help_docs/help_txt/bioconductor-multtest.help.txt b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/bioconductor-multtest.help.txt new file mode 100644 index 0000000000000000000000000000000000000000..9396ee6dd4e0ed35a6252ddcf0e196deafac3795 --- /dev/null +++ b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/bioconductor-multtest.help.txt @@ -0,0 +1,6 @@ +$ conda run -n bioenv_r_bioc Rscript --help +[rc=127] + +Rscript: error while loading shared libraries: libgfortran.so.3: cannot open shared object file: No such file or directory + +ERROR conda.cli.main_run:execute(127): `conda run Rscript --help` failed. (See above for error) diff --git a/BioScientist/agent_system/toolbase/output/help_docs/help_txt/bioconductor-org.ce.eg.db.help.txt b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/bioconductor-org.ce.eg.db.help.txt new file mode 100644 index 0000000000000000000000000000000000000000..9396ee6dd4e0ed35a6252ddcf0e196deafac3795 --- /dev/null +++ b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/bioconductor-org.ce.eg.db.help.txt @@ -0,0 +1,6 @@ +$ conda run -n bioenv_r_bioc Rscript --help +[rc=127] + +Rscript: error while loading shared libraries: libgfortran.so.3: cannot open shared object file: No such file or directory + +ERROR conda.cli.main_run:execute(127): `conda run Rscript --help` failed. (See above for error) diff --git a/BioScientist/agent_system/toolbase/output/help_docs/help_txt/bioconductor-org.mm.eg.db.help.txt b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/bioconductor-org.mm.eg.db.help.txt new file mode 100644 index 0000000000000000000000000000000000000000..9396ee6dd4e0ed35a6252ddcf0e196deafac3795 --- /dev/null +++ b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/bioconductor-org.mm.eg.db.help.txt @@ -0,0 +1,6 @@ +$ conda run -n bioenv_r_bioc Rscript --help +[rc=127] + +Rscript: error while loading shared libraries: libgfortran.so.3: cannot open shared object file: No such file or directory + +ERROR conda.cli.main_run:execute(127): `conda run Rscript --help` failed. (See above for error) diff --git a/BioScientist/agent_system/toolbase/output/help_docs/help_txt/bioconductor-phemd.help.txt b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/bioconductor-phemd.help.txt new file mode 100644 index 0000000000000000000000000000000000000000..ba8c3c1d7d1e908b1f302a0542761a1346b180c8 --- /dev/null +++ b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/bioconductor-phemd.help.txt @@ -0,0 +1,25 @@ +$ conda run -n bioenv Rscript --help +[rc=0] + +Usage: /path/to/Rscript [--options] [-e expr [-e expr2 ...] | file] [args] + +--options accepted are + --help Print usage and exit + --version Print version and exit + --verbose Print information on progress + --default-packages=list + Where 'list' is a comma-separated set + of package names, or 'NULL' +or options to R, in addition to --no-echo --no-restore, such as + --save Do save workspace at the end of the session + --no-environ Don't read the site and user environment files + --no-site-file Don't read the site-wide Rprofile + --no-init-file Don't read the user R profile + --restore Do restore previously saved objects at startup + --vanilla Combine --no-save, --no-restore, --no-site-file + --no-init-file and --no-environ + +'file' may contain spaces but not shell metacharacters +Expressions (one or more '-e ') may be used *instead* of 'file' +See also ?Rscript from within R + diff --git a/BioScientist/agent_system/toolbase/output/help_docs/help_txt/bioconductor-rhtslib.help.txt b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/bioconductor-rhtslib.help.txt new file mode 100644 index 0000000000000000000000000000000000000000..9396ee6dd4e0ed35a6252ddcf0e196deafac3795 --- /dev/null +++ b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/bioconductor-rhtslib.help.txt @@ -0,0 +1,6 @@ +$ conda run -n bioenv_r_bioc Rscript --help +[rc=127] + +Rscript: error while loading shared libraries: libgfortran.so.3: cannot open shared object file: No such file or directory + +ERROR conda.cli.main_run:execute(127): `conda run Rscript --help` failed. (See above for error) diff --git a/BioScientist/agent_system/toolbase/output/help_docs/help_txt/bioconductor-scbfa.help.txt b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/bioconductor-scbfa.help.txt new file mode 100644 index 0000000000000000000000000000000000000000..ba8c3c1d7d1e908b1f302a0542761a1346b180c8 --- /dev/null +++ b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/bioconductor-scbfa.help.txt @@ -0,0 +1,25 @@ +$ conda run -n bioenv Rscript --help +[rc=0] + +Usage: /path/to/Rscript [--options] [-e expr [-e expr2 ...] | file] [args] + +--options accepted are + --help Print usage and exit + --version Print version and exit + --verbose Print information on progress + --default-packages=list + Where 'list' is a comma-separated set + of package names, or 'NULL' +or options to R, in addition to --no-echo --no-restore, such as + --save Do save workspace at the end of the session + --no-environ Don't read the site and user environment files + --no-site-file Don't read the site-wide Rprofile + --no-init-file Don't read the user R profile + --restore Do restore previously saved objects at startup + --vanilla Combine --no-save, --no-restore, --no-site-file + --no-init-file and --no-environ + +'file' may contain spaces but not shell metacharacters +Expressions (one or more '-e ') may be used *instead* of 'file' +See also ?Rscript from within R + diff --git a/BioScientist/agent_system/toolbase/output/help_docs/help_txt/bioconductor-scdataviz.help.txt b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/bioconductor-scdataviz.help.txt new file mode 100644 index 0000000000000000000000000000000000000000..ba8c3c1d7d1e908b1f302a0542761a1346b180c8 --- /dev/null +++ b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/bioconductor-scdataviz.help.txt @@ -0,0 +1,25 @@ +$ conda run -n bioenv Rscript --help +[rc=0] + +Usage: /path/to/Rscript [--options] [-e expr [-e expr2 ...] | file] [args] + +--options accepted are + --help Print usage and exit + --version Print version and exit + --verbose Print information on progress + --default-packages=list + Where 'list' is a comma-separated set + of package names, or 'NULL' +or options to R, in addition to --no-echo --no-restore, such as + --save Do save workspace at the end of the session + --no-environ Don't read the site and user environment files + --no-site-file Don't read the site-wide Rprofile + --no-init-file Don't read the user R profile + --restore Do restore previously saved objects at startup + --vanilla Combine --no-save, --no-restore, --no-site-file + --no-init-file and --no-environ + +'file' may contain spaces but not shell metacharacters +Expressions (one or more '-e ') may be used *instead* of 'file' +See also ?Rscript from within R + diff --git a/BioScientist/agent_system/toolbase/output/help_docs/help_txt/bioconductor-spatialdecon.help.txt b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/bioconductor-spatialdecon.help.txt new file mode 100644 index 0000000000000000000000000000000000000000..ba8c3c1d7d1e908b1f302a0542761a1346b180c8 --- /dev/null +++ b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/bioconductor-spatialdecon.help.txt @@ -0,0 +1,25 @@ +$ conda run -n bioenv Rscript --help +[rc=0] + +Usage: /path/to/Rscript [--options] [-e expr [-e expr2 ...] | file] [args] + +--options accepted are + --help Print usage and exit + --version Print version and exit + --verbose Print information on progress + --default-packages=list + Where 'list' is a comma-separated set + of package names, or 'NULL' +or options to R, in addition to --no-echo --no-restore, such as + --save Do save workspace at the end of the session + --no-environ Don't read the site and user environment files + --no-site-file Don't read the site-wide Rprofile + --no-init-file Don't read the user R profile + --restore Do restore previously saved objects at startup + --vanilla Combine --no-save, --no-restore, --no-site-file + --no-init-file and --no-environ + +'file' may contain spaces but not shell metacharacters +Expressions (one or more '-e ') may be used *instead* of 'file' +See also ?Rscript from within R + diff --git a/BioScientist/agent_system/toolbase/output/help_docs/help_txt/blast.help.txt b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/blast.help.txt new file mode 100644 index 0000000000000000000000000000000000000000..18a5c4904976b9f3a8bb7d3532dc6a9859dfa854 --- /dev/null +++ b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/blast.help.txt @@ -0,0 +1,36 @@ +$ conda run -n bioenv_perl perl -h +[rc=0] + +Usage: perl [switches] [--] [programfile] [arguments] + -0[octal] specify record separator (\0, if no argument) + -a autosplit mode with -n or -p (splits $_ into @F) + -C[number/list] enables the listed Unicode features + -c check syntax only (runs BEGIN and CHECK blocks) + -d[:debugger] run program under debugger + -D[number/list] set debugging flags (argument is a bit mask or alphabets) + -e program one line of program (several -e's allowed, omit programfile) + -E program like -e, but enables all optional features + -f don't do $sitelib/sitecustomize.pl at startup + -F/pattern/ split() pattern for -a switch (//'s are optional) + -i[extension] edit <> files in place (makes backup if extension supplied) + -Idirectory specify @INC/#include directory (several -I's allowed) + -l[octal] enable line ending processing, specifies line terminator + -[mM][-]module execute "use/no module..." before executing program + -n assume "while (<>) { ... }" loop around program + -p assume loop like -n but print line also, like sed + -s enable rudimentary parsing for switches after programfile + -S look for programfile using PATH environment variable + -t enable tainting warnings + -T enable tainting checks + -u dump core after parsing program + -U allow unsafe operations + -v print version, patchlevel and license + -V[:variable] print configuration summary (or a single Config.pm variable) + -w enable many useful warnings + -W enable all warnings + -x[directory] ignore text before #!perl line (optionally cd to directory) + -X disable all warnings + +Run 'perldoc perl' for more help with Perl. + + diff --git a/BioScientist/agent_system/toolbase/output/help_docs/help_txt/brooklyn_plot.help.txt b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/brooklyn_plot.help.txt new file mode 100644 index 0000000000000000000000000000000000000000..09e08ea7add5059376b1785fc85fd9edf3344958 --- /dev/null +++ b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/brooklyn_plot.help.txt @@ -0,0 +1,13 @@ +$ conda run -n bioenv brooklyn_plot --help +[rc=1] + +Traceback (most recent call last): + File "/225040511/miniconda3/envs/bioenv/bin/brooklyn_plot", line 6, in + from brooklyn_plot.__main__ import main + File "/225040511/miniconda3/envs/bioenv/lib/python3.11/site-packages/brooklyn_plot/__main__.py", line 16, in + from brooklyn_plot.libs.parse import parseArg + File "/225040511/miniconda3/envs/bioenv/lib/python3.11/site-packages/brooklyn_plot/libs/parse.py", line 5, in + from pkg_resources import get_distribution +ModuleNotFoundError: No module named 'pkg_resources' + +ERROR conda.cli.main_run:execute(127): `conda run brooklyn_plot --help` failed. (See above for error) diff --git a/BioScientist/agent_system/toolbase/output/help_docs/help_txt/celltypist.help.txt b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/celltypist.help.txt new file mode 100644 index 0000000000000000000000000000000000000000..ca0e3bd4fd18fbf64e8ac91a864427973d5f19b0 --- /dev/null +++ b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/celltypist.help.txt @@ -0,0 +1,73 @@ +$ conda run -n bioenv celltypist --help +[rc=0] +Usage: celltypist [OPTIONS] + + Celltypist: a tool for semi-automatic cell type annotation + +Options: + -i, --indata FILE Path to the input count matrix + (.csv/txt/tsv/tab/mtx) or AnnData (.h5ad). + Genes should be provided as gene symbols. + -m, --model TEXT Model used for predictions. If not provided, + default to using the `Immune_All_Low.pkl` + model. + --transpose-input Transpose the input matrix if `-i / + --indata` file is provided in the gene-by- + cell format. Note Celltypist requires the + cell-by-gene format. + -gf, --gene-file PATH Path to the file which stores each gene per + line corresponding to the genes used in the + provided mtx file. Ignored if `-i / + --indata` is not provided in the mtx format. + -cf, --cell-file PATH Path to the file which stores each cell per + line corresponding to the cells used in the + provided mtx file. Ignored if `-i / + --indata` is not provided in the mtx format. + -mo, --mode [best_match|prob_match] + Choose the cell type with the largest + score/probability as the final prediction + (`best_match`), or enable a multi-label + classification (`prob_match`), which assigns + 0 (i.e., unassigned), 1, or >=2 cell type + labels to each query cell. [default: + best_match] + -ps, --p-thres FLOAT Probability threshold for the multi-label + classification. Ignored if `--mode` is + `best_match`. [default: 0.5] + --majority-voting Refine the predicted labels by running the + majority voting classifier after over- + clustering. + -oc, --over-clustering TEXT Input file with the over-clustering result + of one cell per line, or a string key + specifying an existing metadata column in + the AnnData object. If not provided, default + to using a heuristic over-clustering + approach according to the size of input + data. Ignored if `--majority-voting` is not + set. [default: auto] + --use-GPU Whether to use GPU for over clustering on + the basis of `rapids-singlecell`. Ignored if + `--majority-voting` is not set. + -mp, --min-prop FLOAT For the dominant cell type within a + subcluster, the minimum proportion of cells + required to support naming of the subcluster + by this cell type. Ignored if `--majority- + voting` is not set. [default: 0] + -o, --outdir PATH Directory to store the output files and (if + `--plot-results` is set) figures. Default to + the current working directory. + -p, --prefix TEXT Prefix for the output files and (if `--plot- + results` is set) figures. Default to no + prefix used. + --xlsx Merge output tables into a single Excel + (.xlsx). + --plot-results Plot the prediction results as multiple + figures as well. + --update-models Download the latest models from the remote + server. + --show-models Show all the available models and their + descriptions. + --quiet Hide the banner and configuration + information during the run. + --help Show this message and exit. + diff --git a/BioScientist/agent_system/toolbase/output/help_docs/help_txt/constellations.help.txt b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/constellations.help.txt new file mode 100644 index 0000000000000000000000000000000000000000..4280202f4741d5c28488f709f7085d0753267f5c --- /dev/null +++ b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/constellations.help.txt @@ -0,0 +1,10 @@ +$ conda run -n bioenv_cli constellations --help +[rc=1] + +/bin/bash: /225040511/miniconda3/envs/bioenv_cli/lib/libtinfo.so.6: no version information available (required by /bin/bash) +Traceback (most recent call last): + File "/225040511/miniconda3/envs/bioenv_cli/bin/constellations", line 7, in + from constellations.command import main +ModuleNotFoundError: No module named 'constellations.command' + +ERROR conda.cli.main_run:execute(127): `conda run constellations --help` failed. (See above for error) diff --git a/BioScientist/agent_system/toolbase/output/help_docs/help_txt/gatk4.help.txt b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/gatk4.help.txt new file mode 100644 index 0000000000000000000000000000000000000000..566dac5084a826d743610cbedbd8d2db0cc2c25a --- /dev/null +++ b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/gatk4.help.txt @@ -0,0 +1,99 @@ +$ conda run -n bioenv_java java -help +[rc=0] + +Usage: java [options] [args...] + (to execute a class) + or java [options] -jar [args...] + (to execute a jar file) + or java [options] -m [/] [args...] + java [options] --module [/] [args...] + (to execute the main class in a module) + or java [options] [args] + (to execute a single source-file program) + + Arguments following the main class, source file, -jar , + -m or --module / are passed as the arguments to + main class. + + where options include: + + -cp + -classpath + --class-path + A : separated list of directories, JAR archives, + and ZIP archives to search for class files. + -p + --module-path ... + A : separated list of directories, each directory + is a directory of modules. + --upgrade-module-path ... + A : separated list of directories, each directory + is a directory of modules that replace upgradeable + modules in the runtime image + --add-modules [,...] + root modules to resolve in addition to the initial module. + can also be ALL-DEFAULT, ALL-SYSTEM, + ALL-MODULE-PATH. + --list-modules + list observable modules and exit + -d + --describe-module + describe a module and exit + --dry-run create VM and load main class but do not execute main method. + The --dry-run option may be useful for validating the + command-line options such as the module system configuration. + --validate-modules + validate all modules and exit + The --validate-modules option may be useful for finding + conflicts and other errors with modules on the module path. + -D= + set a system property + -verbose:[class|module|gc|jni] + enable verbose output + -version print product version to the error stream and exit + --version print product version to the output stream and exit + -showversion print product version to the error stream and continue + --show-version + print product version to the output stream and continue + --show-module-resolution + show module resolution output during startup + -? -h -help + print this help message to the error stream + --help print this help message to the output stream + -X print help on extra options to the error stream + --help-extra print help on extra options to the output stream + -ea[:...|:] + -enableassertions[:...|:] + enable assertions with specified granularity + -da[:...|:] + -disableassertions[:...|:] + disable assertions with specified granularity + -esa | -enablesystemassertions + enable system assertions + -dsa | -disablesystemassertions + disable system assertions + -agentlib:[=] + load native agent library , e.g. -agentlib:jdwp + see also -agentlib:jdwp=help + -agentpath:[=] + load native agent library by full pathname + -javaagent:[=] + load Java programming language agent, see java.lang.instrument + -splash: + show splash screen with specified image + HiDPI scaled images are automatically supported and used + if available. The unscaled image filename, e.g. image.ext, + should always be passed as the argument to the -splash option. + The most appropriate scaled image provided will be picked up + automatically. + See the SplashScreen API documentation for more information + @argument files + one or more argument files containing options + -disable-@files + prevent further argument file expansion + --enable-preview + allow classes to depend on preview features of this release +To specify an argument for a long option, you can use --= or +-- . + + diff --git a/BioScientist/agent_system/toolbase/output/help_docs/help_txt/goleft.help.txt b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/goleft.help.txt new file mode 100644 index 0000000000000000000000000000000000000000..af1a30a88d95b185077dd92f24972a638c697c46 --- /dev/null +++ b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/goleft.help.txt @@ -0,0 +1,13 @@ +$ conda run -n bioenv_cli goleft --help +[rc=1] +goleft Version: 0.2.6 + +covstats : coverage stats across bams by sampling +depth : parallelize calls to samtools in user-defined windows +depthwed : matricize output from depth to n-sites * n-samples +indexcov : quick coverage estimate using only the bam index +indexsplit : create regions of even coverage across bams/crams +samplename : report samplename(s) from a bam's SM tag + + +ERROR conda.cli.main_run:execute(127): `conda run goleft --help` failed. (See above for error) diff --git a/BioScientist/agent_system/toolbase/output/help_docs/help_txt/k8.help.txt b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/k8.help.txt new file mode 100644 index 0000000000000000000000000000000000000000..324eaef55277c0ea65f5ab3d99690222c796d30d --- /dev/null +++ b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/k8.help.txt @@ -0,0 +1,255 @@ +$ conda run -n bioenv_cli k8 --help +[rc=0] +Usage: + shell [options] -e string + execute string in V8 + shell [options] file1 file2 ... filek + run JavaScript scripts in file1, file2, ..., filek + shell [options] + shell [options] --shell [file1 file2 ... filek] + run an interactive JavaScript shell + d8 [options] file1 file2 ... filek + d8 [options] + d8 [options] --shell [file1 file2 ... filek] + run the new debugging shell + +Options: + --use_strict (enforce strict mode) + type: bool default: false + --es5_readonly (activate correct semantics for inheriting readonliness) + type: bool default: true + --es52_globals (activate new semantics for global var declarations) + type: bool default: true + --harmony_typeof (enable harmony semantics for typeof) + type: bool default: false + --harmony_scoping (enable harmony block scoping) + type: bool default: false + --harmony_modules (enable harmony modules (implies block scoping)) + type: bool default: false + --harmony_proxies (enable harmony proxies) + type: bool default: false + --harmony_collections (enable harmony collections (sets, maps, and weak maps)) + type: bool default: false + --harmony_observation (enable harmony object observation (implies harmony collections) + type: bool default: false + --harmony (enable all harmony features (except typeof)) + type: bool default: false + --packed_arrays (optimizes arrays that have no holes) + type: bool default: true + --smi_only_arrays (tracks arrays with only smi values) + type: bool default: true + --clever_optimizations (Optimize object size, Array shift, DOM strings and string +) + type: bool default: true + --unbox_double_arrays (automatically unbox arrays of doubles) + type: bool default: true + --string_slices (use string slices) + type: bool default: true + --crankshaft (use crankshaft) + type: bool default: true + --hydrogen_filter (optimization filter) + type: string default: + --use_range (use hydrogen range analysis) + type: bool default: true + --eliminate_dead_phis (eliminate dead phis) + type: bool default: true + --use_gvn (use hydrogen global value numbering) + type: bool default: true + --use_canonicalizing (use hydrogen instruction canonicalizing) + type: bool default: true + --use_inlining (use function inlining) + type: bool default: true + --max_inlined_source_size (maximum source size in bytes considered for a single inlining) + type: int default: 600 + --max_inlined_nodes (maximum number of AST nodes considered for a single inlining) + type: int default: 196 + --max_inlined_nodes_cumulative (maximum cumulative number of AST nodes considered for inlining) + type: int default: 196 + --loop_invariant_code_motion (loop invariant code motion) + type: bool default: true + --fast_math (faster (but maybe less accurate) math functions) + type: bool default: true + --collect_megamorphic_maps_from_stub_cache (crankshaft harvests type feedback from stub cache) + type: bool default: true + --hydrogen_stats (print statistics for hydrogen) + type: bool default: false + --trace_hydrogen (trace generated hydrogen to file) + type: bool default: false + --trace_phase (trace generated IR for specified phases) + type: string default: Z + --trace_inlining (trace inlining decisions) + type: bool default: false + --trace_alloc (trace register allocator) + type: bool default: false + --trace_all_uses (trace all use positions) + type: bool default: false + --trace_range (trace range analysis) + type: bool default: false + --trace_gvn (trace global value numbering) + type: bool default: false + --trace_representation (trace representation types) + type: bool default: false + --stress_pointer_maps (pointer map for every instruction) + type: bool default: false + --stress_environments (environment for every instruction) + type: bool default: false + --deopt_every_n_times (deoptimize every n times a deopt point is passed) + type: int default: 0 + --trap_on_deopt (put a break point before deoptimizing) + type: bool default: false + --deoptimize_uncommon_cases (deoptimize uncommon cases) + type: bool default: true + --polymorphic_inlining (polymorphic inlining) + type: bool default: true + --use_osr (use on-stack replacement) + type: bool default: true + --array_bounds_checks_elimination (perform array bounds checks elimination) + type: bool default: true + --array_index_dehoisting (perform array index dehoisting) + type: bool default: true + --dead_code_elimination (use dead code elimination) + type: bool default: true + --trace_dead_code_elimination (trace dead code elimination) + type: bool default: false + --track_allocation_sites (Use allocation site info to reduce transitions) + type: bool default: true + --trace_osr (trace on-stack replacement) + type: bool default: false + --stress_runs (number of stress runs) + type: int default: 0 + --optimize_closures (optimize closures) + type: bool default: true + --lookup_sample_by_shared (when picking a function to optimize, watch for shared function info, not JSFunction itself) + type: bool default: true + --cache_optimized_code (cache optimized code for closures) + type: bool default: true + --inline_construct (inline constructor calls) + type: bool default: true + --inline_arguments (inline functions with arguments object) + type: bool default: true + --inline_accessors (inline JavaScript accessors) + type: bool default: true + --loop_weight (loop weight for representation inference) + type: int default: 1 + --optimize_for_in (optimize functions containing for-in loops) + type: bool default: true + --opt_safe_uint32_operations (allow uint32 values on optimize frames if they are used only in safe operations) + type: bool default: true + --parallel_recompilation (optimizing hot functions asynchronously on a separate thread) + type: bool default: false + --trace_parallel_recompilation (track parallel recompilation) + type: bool default: false + --parallel_recompilation_queue_length (the length of the parallel compilation queue) + type: int default: 2 + --manual_parallel_recompilation (disable automatic optimization) + type: bool default: false + --experimental_profiler (enable all profiler experiments) + type: bool default: true + --watch_ic_patching (profiler considers IC stability) + type: bool default: false + --frame_count (number of stack frames inspected by the profiler) + type: int default: 1 + --self_optimization (primitive functions trigger their own optimization) + type: bool default: false + --direct_self_opt (call recompile stub directly when self-optimizing) + type: bool default: false + --retry_self_opt (re-try self-optimization if it failed) + type: bool default: false + --interrupt_at_exit (insert an interrupt check at function exit) + type: bool default: false + --weighted_back_edges (weight back edges by jump distance for interrupt triggering) + type: bool default: false + --interrupt_budget (execution budget before interrupt is triggered) + type: int default: 5888 + --type_info_threshold (percentage of ICs that must have type info to allow optimization) + type: int default: 15 + --self_opt_count (call count before self-optimization) + type: int default: 130 + --trace_opt_verbose (extra verbose compilation tracing) + type: bool default: false + --debug_code (generate extra code (assertions) for debugging) + type: bool default: false + --code_comments (emit comments in code disassembly) + type: bool default: false + --enable_sse2 (enable use of SSE2 instructions if available) + type: bool default: true + --enable_sse3 (enable use of SSE3 instructions if available) + type: bool default: true + --enable_sse4_1 (enable use of SSE4.1 instructions if available) + type: bool default: true + --enable_cmov (enable use of CMOV instruction if available) + type: bool default: true + --enable_rdtsc (enable use of RDTSC instruction if available) + type: bool default: true + --enable_sahf (enable use of SAHF instruction if available (X64 only)) + type: bool default: true + --enable_vfp3 (enable use of VFP3 instructions if available - this implies enabling ARMv7 and VFP2 instructions (ARM only)) + type: bool default: true + --enable_vfp2 (enable use of VFP2 instructions if available) + type: bool default: true + --enable_armv7 (enable use of ARMv7 instructions if available (ARM only)) + type: bool default: true + --enable_sudiv (enable use of SDIV and UDIV instructions if available (ARM only)) + type: bool default: true + --enable_movw_movt (enable loading 32-bit constant by means of movw/movt instruction pairs (ARM only)) + type: bool default: false + --enable_unaligned_accesses (enable unaligned accesses for ARMv7 (ARM only)) + type: bool default: true + --enable_fpu (enable use of MIPS FPU instructions if available (MIPS only)) + type: bool default: true + --enable_vldr_imm (enable use of constant pools for double immediate (ARM only)) + type: bool default: false + --expose_natives_as (expose natives in global object) + type: string default: NULL + --expose_debug_as (expose debug in global object) + type: string default: NULL + --expose_gc (expose gc extension) + type: bool default: false + --expose_externalize_string (expose externalize string extension) + type: bool default: false + --stack_trace_limit (number of stack frames to capture) + type: int default: 10 + --builtins_in_stack_traces (show built-in functions in stack traces) + type: bool default: false + --disable_native_files (disable builtin natives files) + type: bool default: false + --inline_new (use fast inline allocation) + type: bool default: true + --stack_trace_on_abort (print a stack trace if an assertion failure occurs) + type: bool default: true + --trace (trace function calls) + type: bool default: false + --mask_constants_with_cookie (use random jit cookie to mask large constants) + type: bool default: true + --lazy (use lazy compilation) + type: bool default: true + --trace_opt (trace lazy optimization) + type: bool default: false + --trace_opt_stats (trace lazy optimization statistics) + type: bool default: false + --opt (use adaptive optimizations) + type: bool default: true + --always_opt (always try to optimize functions) + type: bool default: false + --prepare_always_opt (prepare for turning on always opt) + type: bool default: false + --trace_deopt (trace deoptimization) + type: bool default: false + --min_preparse_length (minimum length for automatic enable preparsing) + type: int default: 1024 + --always_full_compiler (try to use the dedicated run-once backend for all code) + type: bool default: false + --max_opt_count (maximum number of optimization attempts before giving up.) + type: int default: 10 + --compilation_cache (enable compilation cache) + type: bool default: true + --cache_prototype_transitions (cache prototype transitions) + type: bool default: true + --trace_debug_json (trace debugging JSON request/response) + type: bool default: false + --debugger_auto_break (automatically set the debug break flag when debugger commands are in the queue) + type: bool default: true + --enable_liveedit (enable liveedit experimental feature) + type: bool default: true + --break_on_abort (always cause a debug break before aborting) + type: bool default: true + --stack_size \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/output/help_docs/help_txt/kaiju.help.txt b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/kaiju.help.txt new file mode 100644 index 0000000000000000000000000000000000000000..18a5c4904976b9f3a8bb7d3532dc6a9859dfa854 --- /dev/null +++ b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/kaiju.help.txt @@ -0,0 +1,36 @@ +$ conda run -n bioenv_perl perl -h +[rc=0] + +Usage: perl [switches] [--] [programfile] [arguments] + -0[octal] specify record separator (\0, if no argument) + -a autosplit mode with -n or -p (splits $_ into @F) + -C[number/list] enables the listed Unicode features + -c check syntax only (runs BEGIN and CHECK blocks) + -d[:debugger] run program under debugger + -D[number/list] set debugging flags (argument is a bit mask or alphabets) + -e program one line of program (several -e's allowed, omit programfile) + -E program like -e, but enables all optional features + -f don't do $sitelib/sitecustomize.pl at startup + -F/pattern/ split() pattern for -a switch (//'s are optional) + -i[extension] edit <> files in place (makes backup if extension supplied) + -Idirectory specify @INC/#include directory (several -I's allowed) + -l[octal] enable line ending processing, specifies line terminator + -[mM][-]module execute "use/no module..." before executing program + -n assume "while (<>) { ... }" loop around program + -p assume loop like -n but print line also, like sed + -s enable rudimentary parsing for switches after programfile + -S look for programfile using PATH environment variable + -t enable tainting warnings + -T enable tainting checks + -u dump core after parsing program + -U allow unsafe operations + -v print version, patchlevel and license + -V[:variable] print configuration summary (or a single Config.pm variable) + -w enable many useful warnings + -W enable all warnings + -x[directory] ignore text before #!perl line (optionally cd to directory) + -X disable all warnings + +Run 'perldoc perl' for more help with Perl. + + diff --git a/BioScientist/agent_system/toolbase/output/help_docs/help_txt/kmc.help.txt b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/kmc.help.txt new file mode 100644 index 0000000000000000000000000000000000000000..51fc6bd0ee16383e0873ea2f87efacc709d836cf --- /dev/null +++ b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/kmc.help.txt @@ -0,0 +1,37 @@ +$ conda run -n bioenv_cli kmc --help +[rc=0] +K-Mer Counter (KMC) ver. 3.2.4 (2024-02-09) +Usage: + kmc [options] + kmc [options] <@input_file_names> +Parameters: + input_file_name - single file in specified (-f switch) format (gziped or not) + @input_file_names - file name with list of input files in specified (-f switch) format (gziped or not) +Options: + -v - verbose mode (shows all parameter settings); default: false + -k - k-mer length (k from 1 to 256; default: 25) + -m - max amount of RAM in GB (from 1 to 1024); default: 12 + -sm - use strict memory mode (memory limit from -m switch will not be exceeded) + -hc - count homopolymer compressed k-mers (approximate and experimental) + -p - signature length (5, 6, 7, 8, 9, 10, 11); default: 9 + -f - input in FASTA format (-fa), FASTQ format (-fq), multi FASTA (-fm) or BAM (-fbam) or KMC(-fkmc); default: FASTQ + -ci - exclude k-mers occurring less than times (default: 2) + -cs - maximal value of a counter (default: 255) + -cx - exclude k-mers occurring more of than times (default: 1e9) + -b - turn off transformation of k-mers into canonical form + -r - turn on RAM-only mode + -n - number of bins + -t - total number of threads (default: no. of CPU cores) + -sf - number of FASTQ reading threads + -sp - number of splitting threads + -sr - number of threads for 2nd stage + -j - file name with execution summary in JSON format + -w - without output + -o - output in KMC of KFF format; default: KMC + -hp - hide percentage progress (default: false) + -e - only estimate histogram of k-mers occurrences instead of exact k-mer counting + --opt-out-size - optimize output database size (may increase running time) +Example: +kmc -k27 -m24 NA19238.fastq NA.res /data/kmc_tmp_dir/ +kmc -k27 -m24 @files.lst NA.res /data/kmc_tmp_dir/ + diff --git a/BioScientist/agent_system/toolbase/output/help_docs/help_txt/mlst.help.txt b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/mlst.help.txt new file mode 100644 index 0000000000000000000000000000000000000000..18a5c4904976b9f3a8bb7d3532dc6a9859dfa854 --- /dev/null +++ b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/mlst.help.txt @@ -0,0 +1,36 @@ +$ conda run -n bioenv_perl perl -h +[rc=0] + +Usage: perl [switches] [--] [programfile] [arguments] + -0[octal] specify record separator (\0, if no argument) + -a autosplit mode with -n or -p (splits $_ into @F) + -C[number/list] enables the listed Unicode features + -c check syntax only (runs BEGIN and CHECK blocks) + -d[:debugger] run program under debugger + -D[number/list] set debugging flags (argument is a bit mask or alphabets) + -e program one line of program (several -e's allowed, omit programfile) + -E program like -e, but enables all optional features + -f don't do $sitelib/sitecustomize.pl at startup + -F/pattern/ split() pattern for -a switch (//'s are optional) + -i[extension] edit <> files in place (makes backup if extension supplied) + -Idirectory specify @INC/#include directory (several -I's allowed) + -l[octal] enable line ending processing, specifies line terminator + -[mM][-]module execute "use/no module..." before executing program + -n assume "while (<>) { ... }" loop around program + -p assume loop like -n but print line also, like sed + -s enable rudimentary parsing for switches after programfile + -S look for programfile using PATH environment variable + -t enable tainting warnings + -T enable tainting checks + -u dump core after parsing program + -U allow unsafe operations + -v print version, patchlevel and license + -V[:variable] print configuration summary (or a single Config.pm variable) + -w enable many useful warnings + -W enable all warnings + -x[directory] ignore text before #!perl line (optionally cd to directory) + -X disable all warnings + +Run 'perldoc perl' for more help with Perl. + + diff --git a/BioScientist/agent_system/toolbase/output/help_docs/help_txt/multivelo.help.txt b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/multivelo.help.txt new file mode 100644 index 0000000000000000000000000000000000000000..2d8d281887bdcd4b54c949596daca3d3475f3508 --- /dev/null +++ b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/multivelo.help.txt @@ -0,0 +1,43 @@ +$ conda run -n bioenv python -m multivelo --help +[rc=1] + +/225040511/miniconda3/envs/bioenv/lib/python3.11/site-packages/anndata/utils.py:434: FutureWarning: Importing read_csv from `anndata` is deprecated. Import anndata.io.read_csv instead. + warnings.warn(msg, FutureWarning) +/225040511/miniconda3/envs/bioenv/lib/python3.11/site-packages/anndata/utils.py:434: FutureWarning: Importing read_excel from `anndata` is deprecated. Import anndata.io.read_excel instead. + warnings.warn(msg, FutureWarning) +/225040511/miniconda3/envs/bioenv/lib/python3.11/site-packages/anndata/utils.py:434: FutureWarning: Importing read_hdf from `anndata` is deprecated. Import anndata.io.read_hdf instead. + warnings.warn(msg, FutureWarning) +/225040511/miniconda3/envs/bioenv/lib/python3.11/site-packages/anndata/utils.py:434: FutureWarning: Importing read_loom from `anndata` is deprecated. Import anndata.io.read_loom instead. + warnings.warn(msg, FutureWarning) +/225040511/miniconda3/envs/bioenv/lib/python3.11/site-packages/anndata/utils.py:434: FutureWarning: Importing read_mtx from `anndata` is deprecated. Import anndata.io.read_mtx instead. + warnings.warn(msg, FutureWarning) +/225040511/miniconda3/envs/bioenv/lib/python3.11/site-packages/anndata/utils.py:434: FutureWarning: Importing read_text from `anndata` is deprecated. Import anndata.io.read_text instead. + warnings.warn(msg, FutureWarning) +/225040511/miniconda3/envs/bioenv/lib/python3.11/site-packages/anndata/utils.py:434: FutureWarning: Importing read_umi_tools from `anndata` is deprecated. Import anndata.io.read_umi_tools instead. + warnings.warn(msg, FutureWarning) +Traceback (most recent call last): + File "", line 189, in _run_module_as_main + File "", line 148, in _get_module_details + File "", line 112, in _get_module_details + File "/225040511/miniconda3/envs/bioenv/lib/python3.11/site-packages/multivelo/__init__.py", line 1, in + from .auxiliary import ( + File "/225040511/miniconda3/envs/bioenv/lib/python3.11/site-packages/multivelo/auxiliary.py", line 7, in + import scvelo as scv + File "/225040511/miniconda3/envs/bioenv/lib/python3.11/site-packages/scvelo/__init__.py", line 5, in + from scvelo import datasets, logging + File "/225040511/miniconda3/envs/bioenv/lib/python3.11/site-packages/scvelo/datasets/__init__.py", line 1, in + from ._datasets import ( + File "/225040511/miniconda3/envs/bioenv/lib/python3.11/site-packages/scvelo/datasets/_datasets.py", line 10, in + from scvelo.core import cleanup + File "/225040511/miniconda3/envs/bioenv/lib/python3.11/site-packages/scvelo/core/__init__.py", line 1, in + from ._anndata import ( + File "/225040511/miniconda3/envs/bioenv/lib/python3.11/site-packages/scvelo/core/_anndata.py", line 15, in + from scvelo import logging as logg + File "/225040511/miniconda3/envs/bioenv/lib/python3.11/site-packages/scvelo/logging.py", line 12, in + from scvelo import settings + File "/225040511/miniconda3/envs/bioenv/lib/python3.11/site-packages/scvelo/settings.py", line 91, in + warnings.filterwarnings("ignore", category=cbook.mplDeprecation) + ^^^^^^^^^^^^^^^^^^^^ +AttributeError: module 'matplotlib.cbook' has no attribute 'mplDeprecation' + +ERROR conda.cli.main_run:execute(127): `conda run python -m multivelo --help` failed. (See above for error) diff --git a/BioScientist/agent_system/toolbase/output/help_docs/help_txt/pandaseq.help.txt b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/pandaseq.help.txt new file mode 100644 index 0000000000000000000000000000000000000000..241ff457a60e60fea885860a063931381e1bfac2 --- /dev/null +++ b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/pandaseq.help.txt @@ -0,0 +1,7 @@ +$ conda run -n bioenv_cli pandaseq --help +[rc=1] + +pandaseq: invalid option -- '-' +Unknown option `--'. + +ERROR conda.cli.main_run:execute(127): `conda run pandaseq --help` failed. (See above for error) diff --git a/BioScientist/agent_system/toolbase/output/help_docs/help_txt/pango-designation.help.txt b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/pango-designation.help.txt new file mode 100644 index 0000000000000000000000000000000000000000..9c5f3ab33002e79bc9f9866fc4b0bbf69e1e08ef --- /dev/null +++ b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/pango-designation.help.txt @@ -0,0 +1,9 @@ +$ conda run -n bioenv_cli pango_designation --help +[rc=1] + +Traceback (most recent call last): + File "/225040511/miniconda3/envs/bioenv_cli/bin/pango_designation", line 6, in + from pango_designation.command import main +ModuleNotFoundError: No module named 'pango_designation.command' + +ERROR conda.cli.main_run:execute(127): `conda run pango_designation --help` failed. (See above for error) diff --git a/BioScientist/agent_system/toolbase/output/help_docs/help_txt/parallel.help.txt b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/parallel.help.txt new file mode 100644 index 0000000000000000000000000000000000000000..b557cc88b4ec175b98f61b4d382eb88c0a589176 --- /dev/null +++ b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/parallel.help.txt @@ -0,0 +1,42 @@ +$ conda run -n bioenv_cli parallel --help +[rc=255] +Usage: + +parallel [options] [command [arguments]] < list_of_arguments +parallel [options] [command [arguments]] (::: arguments|:::: argfile(s))... +cat ... | parallel --pipe [options] [command [arguments]] + +-j n Run n jobs in parallel +-k Keep same order +-X Multiple arguments with context replace +--colsep regexp Split input on regexp for positional replacements +{} {.} {/} {/.} {#} {%} {= perl code =} Replacement strings +{3} {3.} {3/} {3/.} {=3 perl code =} Positional replacement strings +With --plus: {} = {+/}/{/} = {.}.{+.} = {+/}/{/.}.{+.} = {..}.{+..} = + {+/}/{/..}.{+..} = {...}.{+...} = {+/}/{/...}.{+...} + +-S sshlogin Example: foo@server.example.com +--slf .. Use ~/.parallel/sshloginfile as the list of sshlogins +--trc {}.bar Shorthand for --transfer --return {}.bar --cleanup +--onall Run the given command with argument on all sshlogins +--nonall Run the given command with no arguments on all sshlogins + +--pipe Split stdin (standard input) to multiple jobs. +--recend str Record end separator for --pipe. +--recstart str Record start separator for --pipe. + +See 'man parallel' for details + +Academic tradition requires you to cite works you base your article on. +When using programs that use GNU Parallel to process data for publication +please cite: + + O. Tange (2011): GNU Parallel - The Command-Line Power Tool, + ;login: The USENIX Magazine, February 2011:42-47. + +This helps funding further development; AND IT WON'T COST YOU A CENT. +If you pay 10000 EUR you should feel free to use GNU Parallel without citing. + + + +ERROR conda.cli.main_run:execute(127): `conda run parallel --help` failed. (See above for error) diff --git a/BioScientist/agent_system/toolbase/output/help_docs/help_txt/peptide-shaker.help.txt b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/peptide-shaker.help.txt new file mode 100644 index 0000000000000000000000000000000000000000..0b8f7529448a392b672eab376bb9b4411884c457 --- /dev/null +++ b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/peptide-shaker.help.txt @@ -0,0 +1,102 @@ +$ conda run -n bioenv java -help +[rc=0] + +Usage: java [options] [args...] + (to execute a class) + or java [options] -jar [args...] + (to execute a jar file) + or java [options] -m [/] [args...] + java [options] --module [/] [args...] + (to execute the main class in a module) + or java [options] [args] + (to execute a single source-file program) + + Arguments following the main class, source file, -jar , + -m or --module / are passed as the arguments to + main class. + + where options include: + + -cp + -classpath + --class-path + A : separated list of directories, JAR archives, + and ZIP archives to search for class files. + -p + --module-path ... + A : separated list of directories, each directory + is a directory of modules. + --upgrade-module-path ... + A : separated list of directories, each directory + is a directory of modules that replace upgradeable + modules in the runtime image + --add-modules [,...] + root modules to resolve in addition to the initial module. + can also be ALL-DEFAULT, ALL-SYSTEM, + ALL-MODULE-PATH. + --enable-native-access [,...] + modules that are permitted to perform restricted native operations. + can also be ALL-UNNAMED. + --list-modules + list observable modules and exit + -d + --describe-module + describe a module and exit + --dry-run create VM and load main class but do not execute main method. + The --dry-run option may be useful for validating the + command-line options such as the module system configuration. + --validate-modules + validate all modules and exit + The --validate-modules option may be useful for finding + conflicts and other errors with modules on the module path. + -D= + set a system property + -verbose:[class|module|gc|jni] + enable verbose output for the given subsystem + -version print product version to the error stream and exit + --version print product version to the output stream and exit + -showversion print product version to the error stream and continue + --show-version + print product version to the output stream and continue + --show-module-resolution + show module resolution output during startup + -? -h -help + print this help message to the error stream + --help print this help message to the output stream + -X print help on extra options to the error stream + --help-extra print help on extra options to the output stream + -ea[:...|:] + -enableassertions[:...|:] + enable assertions with specified granularity + -da[:...|:] + -disableassertions[:...|:] + disable assertions with specified granularity + -esa | -enablesystemassertions + enable system assertions + -dsa | -disablesystemassertions + disable system assertions + -agentlib:[=] + load native agent library , e.g. -agentlib:jdwp + see also -agentlib:jdwp=help + -agentpath:[=] + load native agent library by full pathname + -javaagent:[=] + load Java programming language agent, see java.lang.instrument + -splash: + show splash screen with specified image + HiDPI scaled images are automatically supported and used + if available. The unscaled image filename, e.g. image.ext, + should always be passed as the argument to the -splash option. + The most appropriate scaled image provided will be picked up + automatically. + See the SplashScreen API documentation for more information + @argument files + one or more argument files containing options + -disable-@files + prevent further argument file expansion + --enable-preview + allow classes to depend on preview features of this release +To specify an argument for a long option, you can use --= or +-- . + + diff --git a/BioScientist/agent_system/toolbase/output/help_docs/help_txt/perl-array-compare.help.txt b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/perl-array-compare.help.txt new file mode 100644 index 0000000000000000000000000000000000000000..18a5c4904976b9f3a8bb7d3532dc6a9859dfa854 --- /dev/null +++ b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/perl-array-compare.help.txt @@ -0,0 +1,36 @@ +$ conda run -n bioenv_perl perl -h +[rc=0] + +Usage: perl [switches] [--] [programfile] [arguments] + -0[octal] specify record separator (\0, if no argument) + -a autosplit mode with -n or -p (splits $_ into @F) + -C[number/list] enables the listed Unicode features + -c check syntax only (runs BEGIN and CHECK blocks) + -d[:debugger] run program under debugger + -D[number/list] set debugging flags (argument is a bit mask or alphabets) + -e program one line of program (several -e's allowed, omit programfile) + -E program like -e, but enables all optional features + -f don't do $sitelib/sitecustomize.pl at startup + -F/pattern/ split() pattern for -a switch (//'s are optional) + -i[extension] edit <> files in place (makes backup if extension supplied) + -Idirectory specify @INC/#include directory (several -I's allowed) + -l[octal] enable line ending processing, specifies line terminator + -[mM][-]module execute "use/no module..." before executing program + -n assume "while (<>) { ... }" loop around program + -p assume loop like -n but print line also, like sed + -s enable rudimentary parsing for switches after programfile + -S look for programfile using PATH environment variable + -t enable tainting warnings + -T enable tainting checks + -u dump core after parsing program + -U allow unsafe operations + -v print version, patchlevel and license + -V[:variable] print configuration summary (or a single Config.pm variable) + -w enable many useful warnings + -W enable all warnings + -x[directory] ignore text before #!perl line (optionally cd to directory) + -X disable all warnings + +Run 'perldoc perl' for more help with Perl. + + diff --git a/BioScientist/agent_system/toolbase/output/help_docs/help_txt/perl-bio-phylo.help.txt b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/perl-bio-phylo.help.txt new file mode 100644 index 0000000000000000000000000000000000000000..18a5c4904976b9f3a8bb7d3532dc6a9859dfa854 --- /dev/null +++ b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/perl-bio-phylo.help.txt @@ -0,0 +1,36 @@ +$ conda run -n bioenv_perl perl -h +[rc=0] + +Usage: perl [switches] [--] [programfile] [arguments] + -0[octal] specify record separator (\0, if no argument) + -a autosplit mode with -n or -p (splits $_ into @F) + -C[number/list] enables the listed Unicode features + -c check syntax only (runs BEGIN and CHECK blocks) + -d[:debugger] run program under debugger + -D[number/list] set debugging flags (argument is a bit mask or alphabets) + -e program one line of program (several -e's allowed, omit programfile) + -E program like -e, but enables all optional features + -f don't do $sitelib/sitecustomize.pl at startup + -F/pattern/ split() pattern for -a switch (//'s are optional) + -i[extension] edit <> files in place (makes backup if extension supplied) + -Idirectory specify @INC/#include directory (several -I's allowed) + -l[octal] enable line ending processing, specifies line terminator + -[mM][-]module execute "use/no module..." before executing program + -n assume "while (<>) { ... }" loop around program + -p assume loop like -n but print line also, like sed + -s enable rudimentary parsing for switches after programfile + -S look for programfile using PATH environment variable + -t enable tainting warnings + -T enable tainting checks + -u dump core after parsing program + -U allow unsafe operations + -v print version, patchlevel and license + -V[:variable] print configuration summary (or a single Config.pm variable) + -w enable many useful warnings + -W enable all warnings + -x[directory] ignore text before #!perl line (optionally cd to directory) + -X disable all warnings + +Run 'perldoc perl' for more help with Perl. + + diff --git a/BioScientist/agent_system/toolbase/output/help_docs/help_txt/perl-bioperl-run.help.txt b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/perl-bioperl-run.help.txt new file mode 100644 index 0000000000000000000000000000000000000000..18a5c4904976b9f3a8bb7d3532dc6a9859dfa854 --- /dev/null +++ b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/perl-bioperl-run.help.txt @@ -0,0 +1,36 @@ +$ conda run -n bioenv_perl perl -h +[rc=0] + +Usage: perl [switches] [--] [programfile] [arguments] + -0[octal] specify record separator (\0, if no argument) + -a autosplit mode with -n or -p (splits $_ into @F) + -C[number/list] enables the listed Unicode features + -c check syntax only (runs BEGIN and CHECK blocks) + -d[:debugger] run program under debugger + -D[number/list] set debugging flags (argument is a bit mask or alphabets) + -e program one line of program (several -e's allowed, omit programfile) + -E program like -e, but enables all optional features + -f don't do $sitelib/sitecustomize.pl at startup + -F/pattern/ split() pattern for -a switch (//'s are optional) + -i[extension] edit <> files in place (makes backup if extension supplied) + -Idirectory specify @INC/#include directory (several -I's allowed) + -l[octal] enable line ending processing, specifies line terminator + -[mM][-]module execute "use/no module..." before executing program + -n assume "while (<>) { ... }" loop around program + -p assume loop like -n but print line also, like sed + -s enable rudimentary parsing for switches after programfile + -S look for programfile using PATH environment variable + -t enable tainting warnings + -T enable tainting checks + -u dump core after parsing program + -U allow unsafe operations + -v print version, patchlevel and license + -V[:variable] print configuration summary (or a single Config.pm variable) + -w enable many useful warnings + -W enable all warnings + -x[directory] ignore text before #!perl line (optionally cd to directory) + -X disable all warnings + +Run 'perldoc perl' for more help with Perl. + + diff --git a/BioScientist/agent_system/toolbase/output/help_docs/help_txt/perl-capture-tiny.help.txt b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/perl-capture-tiny.help.txt new file mode 100644 index 0000000000000000000000000000000000000000..18a5c4904976b9f3a8bb7d3532dc6a9859dfa854 --- /dev/null +++ b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/perl-capture-tiny.help.txt @@ -0,0 +1,36 @@ +$ conda run -n bioenv_perl perl -h +[rc=0] + +Usage: perl [switches] [--] [programfile] [arguments] + -0[octal] specify record separator (\0, if no argument) + -a autosplit mode with -n or -p (splits $_ into @F) + -C[number/list] enables the listed Unicode features + -c check syntax only (runs BEGIN and CHECK blocks) + -d[:debugger] run program under debugger + -D[number/list] set debugging flags (argument is a bit mask or alphabets) + -e program one line of program (several -e's allowed, omit programfile) + -E program like -e, but enables all optional features + -f don't do $sitelib/sitecustomize.pl at startup + -F/pattern/ split() pattern for -a switch (//'s are optional) + -i[extension] edit <> files in place (makes backup if extension supplied) + -Idirectory specify @INC/#include directory (several -I's allowed) + -l[octal] enable line ending processing, specifies line terminator + -[mM][-]module execute "use/no module..." before executing program + -n assume "while (<>) { ... }" loop around program + -p assume loop like -n but print line also, like sed + -s enable rudimentary parsing for switches after programfile + -S look for programfile using PATH environment variable + -t enable tainting warnings + -T enable tainting checks + -u dump core after parsing program + -U allow unsafe operations + -v print version, patchlevel and license + -V[:variable] print configuration summary (or a single Config.pm variable) + -w enable many useful warnings + -W enable all warnings + -x[directory] ignore text before #!perl line (optionally cd to directory) + -X disable all warnings + +Run 'perldoc perl' for more help with Perl. + + diff --git a/BioScientist/agent_system/toolbase/output/help_docs/help_txt/perl-convert-binary-c.help.txt b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/perl-convert-binary-c.help.txt new file mode 100644 index 0000000000000000000000000000000000000000..18a5c4904976b9f3a8bb7d3532dc6a9859dfa854 --- /dev/null +++ b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/perl-convert-binary-c.help.txt @@ -0,0 +1,36 @@ +$ conda run -n bioenv_perl perl -h +[rc=0] + +Usage: perl [switches] [--] [programfile] [arguments] + -0[octal] specify record separator (\0, if no argument) + -a autosplit mode with -n or -p (splits $_ into @F) + -C[number/list] enables the listed Unicode features + -c check syntax only (runs BEGIN and CHECK blocks) + -d[:debugger] run program under debugger + -D[number/list] set debugging flags (argument is a bit mask or alphabets) + -e program one line of program (several -e's allowed, omit programfile) + -E program like -e, but enables all optional features + -f don't do $sitelib/sitecustomize.pl at startup + -F/pattern/ split() pattern for -a switch (//'s are optional) + -i[extension] edit <> files in place (makes backup if extension supplied) + -Idirectory specify @INC/#include directory (several -I's allowed) + -l[octal] enable line ending processing, specifies line terminator + -[mM][-]module execute "use/no module..." before executing program + -n assume "while (<>) { ... }" loop around program + -p assume loop like -n but print line also, like sed + -s enable rudimentary parsing for switches after programfile + -S look for programfile using PATH environment variable + -t enable tainting warnings + -T enable tainting checks + -u dump core after parsing program + -U allow unsafe operations + -v print version, patchlevel and license + -V[:variable] print configuration summary (or a single Config.pm variable) + -w enable many useful warnings + -W enable all warnings + -x[directory] ignore text before #!perl line (optionally cd to directory) + -X disable all warnings + +Run 'perldoc perl' for more help with Perl. + + diff --git a/BioScientist/agent_system/toolbase/output/help_docs/help_txt/perl-crypt-rc4.help.txt b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/perl-crypt-rc4.help.txt new file mode 100644 index 0000000000000000000000000000000000000000..18a5c4904976b9f3a8bb7d3532dc6a9859dfa854 --- /dev/null +++ b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/perl-crypt-rc4.help.txt @@ -0,0 +1,36 @@ +$ conda run -n bioenv_perl perl -h +[rc=0] + +Usage: perl [switches] [--] [programfile] [arguments] + -0[octal] specify record separator (\0, if no argument) + -a autosplit mode with -n or -p (splits $_ into @F) + -C[number/list] enables the listed Unicode features + -c check syntax only (runs BEGIN and CHECK blocks) + -d[:debugger] run program under debugger + -D[number/list] set debugging flags (argument is a bit mask or alphabets) + -e program one line of program (several -e's allowed, omit programfile) + -E program like -e, but enables all optional features + -f don't do $sitelib/sitecustomize.pl at startup + -F/pattern/ split() pattern for -a switch (//'s are optional) + -i[extension] edit <> files in place (makes backup if extension supplied) + -Idirectory specify @INC/#include directory (several -I's allowed) + -l[octal] enable line ending processing, specifies line terminator + -[mM][-]module execute "use/no module..." before executing program + -n assume "while (<>) { ... }" loop around program + -p assume loop like -n but print line also, like sed + -s enable rudimentary parsing for switches after programfile + -S look for programfile using PATH environment variable + -t enable tainting warnings + -T enable tainting checks + -u dump core after parsing program + -U allow unsafe operations + -v print version, patchlevel and license + -V[:variable] print configuration summary (or a single Config.pm variable) + -w enable many useful warnings + -W enable all warnings + -x[directory] ignore text before #!perl line (optionally cd to directory) + -X disable all warnings + +Run 'perldoc perl' for more help with Perl. + + diff --git a/BioScientist/agent_system/toolbase/output/help_docs/help_txt/perl-date-format.help.txt b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/perl-date-format.help.txt new file mode 100644 index 0000000000000000000000000000000000000000..18a5c4904976b9f3a8bb7d3532dc6a9859dfa854 --- /dev/null +++ b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/perl-date-format.help.txt @@ -0,0 +1,36 @@ +$ conda run -n bioenv_perl perl -h +[rc=0] + +Usage: perl [switches] [--] [programfile] [arguments] + -0[octal] specify record separator (\0, if no argument) + -a autosplit mode with -n or -p (splits $_ into @F) + -C[number/list] enables the listed Unicode features + -c check syntax only (runs BEGIN and CHECK blocks) + -d[:debugger] run program under debugger + -D[number/list] set debugging flags (argument is a bit mask or alphabets) + -e program one line of program (several -e's allowed, omit programfile) + -E program like -e, but enables all optional features + -f don't do $sitelib/sitecustomize.pl at startup + -F/pattern/ split() pattern for -a switch (//'s are optional) + -i[extension] edit <> files in place (makes backup if extension supplied) + -Idirectory specify @INC/#include directory (several -I's allowed) + -l[octal] enable line ending processing, specifies line terminator + -[mM][-]module execute "use/no module..." before executing program + -n assume "while (<>) { ... }" loop around program + -p assume loop like -n but print line also, like sed + -s enable rudimentary parsing for switches after programfile + -S look for programfile using PATH environment variable + -t enable tainting warnings + -T enable tainting checks + -u dump core after parsing program + -U allow unsafe operations + -v print version, patchlevel and license + -V[:variable] print configuration summary (or a single Config.pm variable) + -w enable many useful warnings + -W enable all warnings + -x[directory] ignore text before #!perl line (optionally cd to directory) + -X disable all warnings + +Run 'perldoc perl' for more help with Perl. + + diff --git a/BioScientist/agent_system/toolbase/output/help_docs/help_txt/perl-digest-hmac.help.txt b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/perl-digest-hmac.help.txt new file mode 100644 index 0000000000000000000000000000000000000000..18a5c4904976b9f3a8bb7d3532dc6a9859dfa854 --- /dev/null +++ b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/perl-digest-hmac.help.txt @@ -0,0 +1,36 @@ +$ conda run -n bioenv_perl perl -h +[rc=0] + +Usage: perl [switches] [--] [programfile] [arguments] + -0[octal] specify record separator (\0, if no argument) + -a autosplit mode with -n or -p (splits $_ into @F) + -C[number/list] enables the listed Unicode features + -c check syntax only (runs BEGIN and CHECK blocks) + -d[:debugger] run program under debugger + -D[number/list] set debugging flags (argument is a bit mask or alphabets) + -e program one line of program (several -e's allowed, omit programfile) + -E program like -e, but enables all optional features + -f don't do $sitelib/sitecustomize.pl at startup + -F/pattern/ split() pattern for -a switch (//'s are optional) + -i[extension] edit <> files in place (makes backup if extension supplied) + -Idirectory specify @INC/#include directory (several -I's allowed) + -l[octal] enable line ending processing, specifies line terminator + -[mM][-]module execute "use/no module..." before executing program + -n assume "while (<>) { ... }" loop around program + -p assume loop like -n but print line also, like sed + -s enable rudimentary parsing for switches after programfile + -S look for programfile using PATH environment variable + -t enable tainting warnings + -T enable tainting checks + -u dump core after parsing program + -U allow unsafe operations + -v print version, patchlevel and license + -V[:variable] print configuration summary (or a single Config.pm variable) + -w enable many useful warnings + -W enable all warnings + -x[directory] ignore text before #!perl line (optionally cd to directory) + -X disable all warnings + +Run 'perldoc perl' for more help with Perl. + + diff --git a/BioScientist/agent_system/toolbase/output/help_docs/help_txt/perl-dist-checkconflicts.help.txt b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/perl-dist-checkconflicts.help.txt new file mode 100644 index 0000000000000000000000000000000000000000..18a5c4904976b9f3a8bb7d3532dc6a9859dfa854 --- /dev/null +++ b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/perl-dist-checkconflicts.help.txt @@ -0,0 +1,36 @@ +$ conda run -n bioenv_perl perl -h +[rc=0] + +Usage: perl [switches] [--] [programfile] [arguments] + -0[octal] specify record separator (\0, if no argument) + -a autosplit mode with -n or -p (splits $_ into @F) + -C[number/list] enables the listed Unicode features + -c check syntax only (runs BEGIN and CHECK blocks) + -d[:debugger] run program under debugger + -D[number/list] set debugging flags (argument is a bit mask or alphabets) + -e program one line of program (several -e's allowed, omit programfile) + -E program like -e, but enables all optional features + -f don't do $sitelib/sitecustomize.pl at startup + -F/pattern/ split() pattern for -a switch (//'s are optional) + -i[extension] edit <> files in place (makes backup if extension supplied) + -Idirectory specify @INC/#include directory (several -I's allowed) + -l[octal] enable line ending processing, specifies line terminator + -[mM][-]module execute "use/no module..." before executing program + -n assume "while (<>) { ... }" loop around program + -p assume loop like -n but print line also, like sed + -s enable rudimentary parsing for switches after programfile + -S look for programfile using PATH environment variable + -t enable tainting warnings + -T enable tainting checks + -u dump core after parsing program + -U allow unsafe operations + -v print version, patchlevel and license + -V[:variable] print configuration summary (or a single Config.pm variable) + -w enable many useful warnings + -W enable all warnings + -x[directory] ignore text before #!perl line (optionally cd to directory) + -X disable all warnings + +Run 'perldoc perl' for more help with Perl. + + diff --git a/BioScientist/agent_system/toolbase/output/help_docs/help_txt/perl-error.help.txt b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/perl-error.help.txt new file mode 100644 index 0000000000000000000000000000000000000000..18a5c4904976b9f3a8bb7d3532dc6a9859dfa854 --- /dev/null +++ b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/perl-error.help.txt @@ -0,0 +1,36 @@ +$ conda run -n bioenv_perl perl -h +[rc=0] + +Usage: perl [switches] [--] [programfile] [arguments] + -0[octal] specify record separator (\0, if no argument) + -a autosplit mode with -n or -p (splits $_ into @F) + -C[number/list] enables the listed Unicode features + -c check syntax only (runs BEGIN and CHECK blocks) + -d[:debugger] run program under debugger + -D[number/list] set debugging flags (argument is a bit mask or alphabets) + -e program one line of program (several -e's allowed, omit programfile) + -E program like -e, but enables all optional features + -f don't do $sitelib/sitecustomize.pl at startup + -F/pattern/ split() pattern for -a switch (//'s are optional) + -i[extension] edit <> files in place (makes backup if extension supplied) + -Idirectory specify @INC/#include directory (several -I's allowed) + -l[octal] enable line ending processing, specifies line terminator + -[mM][-]module execute "use/no module..." before executing program + -n assume "while (<>) { ... }" loop around program + -p assume loop like -n but print line also, like sed + -s enable rudimentary parsing for switches after programfile + -S look for programfile using PATH environment variable + -t enable tainting warnings + -T enable tainting checks + -u dump core after parsing program + -U allow unsafe operations + -v print version, patchlevel and license + -V[:variable] print configuration summary (or a single Config.pm variable) + -w enable many useful warnings + -W enable all warnings + -x[directory] ignore text before #!perl line (optionally cd to directory) + -X disable all warnings + +Run 'perldoc perl' for more help with Perl. + + diff --git a/BioScientist/agent_system/toolbase/output/help_docs/help_txt/perl-file-path.help.txt b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/perl-file-path.help.txt new file mode 100644 index 0000000000000000000000000000000000000000..18a5c4904976b9f3a8bb7d3532dc6a9859dfa854 --- /dev/null +++ b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/perl-file-path.help.txt @@ -0,0 +1,36 @@ +$ conda run -n bioenv_perl perl -h +[rc=0] + +Usage: perl [switches] [--] [programfile] [arguments] + -0[octal] specify record separator (\0, if no argument) + -a autosplit mode with -n or -p (splits $_ into @F) + -C[number/list] enables the listed Unicode features + -c check syntax only (runs BEGIN and CHECK blocks) + -d[:debugger] run program under debugger + -D[number/list] set debugging flags (argument is a bit mask or alphabets) + -e program one line of program (several -e's allowed, omit programfile) + -E program like -e, but enables all optional features + -f don't do $sitelib/sitecustomize.pl at startup + -F/pattern/ split() pattern for -a switch (//'s are optional) + -i[extension] edit <> files in place (makes backup if extension supplied) + -Idirectory specify @INC/#include directory (several -I's allowed) + -l[octal] enable line ending processing, specifies line terminator + -[mM][-]module execute "use/no module..." before executing program + -n assume "while (<>) { ... }" loop around program + -p assume loop like -n but print line also, like sed + -s enable rudimentary parsing for switches after programfile + -S look for programfile using PATH environment variable + -t enable tainting warnings + -T enable tainting checks + -u dump core after parsing program + -U allow unsafe operations + -v print version, patchlevel and license + -V[:variable] print configuration summary (or a single Config.pm variable) + -w enable many useful warnings + -W enable all warnings + -x[directory] ignore text before #!perl line (optionally cd to directory) + -X disable all warnings + +Run 'perldoc perl' for more help with Perl. + + diff --git a/BioScientist/agent_system/toolbase/output/help_docs/help_txt/perl-file-slurp-tiny.help.txt b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/perl-file-slurp-tiny.help.txt new file mode 100644 index 0000000000000000000000000000000000000000..18a5c4904976b9f3a8bb7d3532dc6a9859dfa854 --- /dev/null +++ b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/perl-file-slurp-tiny.help.txt @@ -0,0 +1,36 @@ +$ conda run -n bioenv_perl perl -h +[rc=0] + +Usage: perl [switches] [--] [programfile] [arguments] + -0[octal] specify record separator (\0, if no argument) + -a autosplit mode with -n or -p (splits $_ into @F) + -C[number/list] enables the listed Unicode features + -c check syntax only (runs BEGIN and CHECK blocks) + -d[:debugger] run program under debugger + -D[number/list] set debugging flags (argument is a bit mask or alphabets) + -e program one line of program (several -e's allowed, omit programfile) + -E program like -e, but enables all optional features + -f don't do $sitelib/sitecustomize.pl at startup + -F/pattern/ split() pattern for -a switch (//'s are optional) + -i[extension] edit <> files in place (makes backup if extension supplied) + -Idirectory specify @INC/#include directory (several -I's allowed) + -l[octal] enable line ending processing, specifies line terminator + -[mM][-]module execute "use/no module..." before executing program + -n assume "while (<>) { ... }" loop around program + -p assume loop like -n but print line also, like sed + -s enable rudimentary parsing for switches after programfile + -S look for programfile using PATH environment variable + -t enable tainting warnings + -T enable tainting checks + -u dump core after parsing program + -U allow unsafe operations + -v print version, patchlevel and license + -V[:variable] print configuration summary (or a single Config.pm variable) + -w enable many useful warnings + -W enable all warnings + -x[directory] ignore text before #!perl line (optionally cd to directory) + -X disable all warnings + +Run 'perldoc perl' for more help with Perl. + + diff --git a/BioScientist/agent_system/toolbase/output/help_docs/help_txt/perl-graphviz.help.txt b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/perl-graphviz.help.txt new file mode 100644 index 0000000000000000000000000000000000000000..18a5c4904976b9f3a8bb7d3532dc6a9859dfa854 --- /dev/null +++ b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/perl-graphviz.help.txt @@ -0,0 +1,36 @@ +$ conda run -n bioenv_perl perl -h +[rc=0] + +Usage: perl [switches] [--] [programfile] [arguments] + -0[octal] specify record separator (\0, if no argument) + -a autosplit mode with -n or -p (splits $_ into @F) + -C[number/list] enables the listed Unicode features + -c check syntax only (runs BEGIN and CHECK blocks) + -d[:debugger] run program under debugger + -D[number/list] set debugging flags (argument is a bit mask or alphabets) + -e program one line of program (several -e's allowed, omit programfile) + -E program like -e, but enables all optional features + -f don't do $sitelib/sitecustomize.pl at startup + -F/pattern/ split() pattern for -a switch (//'s are optional) + -i[extension] edit <> files in place (makes backup if extension supplied) + -Idirectory specify @INC/#include directory (several -I's allowed) + -l[octal] enable line ending processing, specifies line terminator + -[mM][-]module execute "use/no module..." before executing program + -n assume "while (<>) { ... }" loop around program + -p assume loop like -n but print line also, like sed + -s enable rudimentary parsing for switches after programfile + -S look for programfile using PATH environment variable + -t enable tainting warnings + -T enable tainting checks + -u dump core after parsing program + -U allow unsafe operations + -v print version, patchlevel and license + -V[:variable] print configuration summary (or a single Config.pm variable) + -w enable many useful warnings + -W enable all warnings + -x[directory] ignore text before #!perl line (optionally cd to directory) + -X disable all warnings + +Run 'perldoc perl' for more help with Perl. + + diff --git a/BioScientist/agent_system/toolbase/output/help_docs/help_txt/perl-html-treebuilder-xpath.help.txt b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/perl-html-treebuilder-xpath.help.txt new file mode 100644 index 0000000000000000000000000000000000000000..18a5c4904976b9f3a8bb7d3532dc6a9859dfa854 --- /dev/null +++ b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/perl-html-treebuilder-xpath.help.txt @@ -0,0 +1,36 @@ +$ conda run -n bioenv_perl perl -h +[rc=0] + +Usage: perl [switches] [--] [programfile] [arguments] + -0[octal] specify record separator (\0, if no argument) + -a autosplit mode with -n or -p (splits $_ into @F) + -C[number/list] enables the listed Unicode features + -c check syntax only (runs BEGIN and CHECK blocks) + -d[:debugger] run program under debugger + -D[number/list] set debugging flags (argument is a bit mask or alphabets) + -e program one line of program (several -e's allowed, omit programfile) + -E program like -e, but enables all optional features + -f don't do $sitelib/sitecustomize.pl at startup + -F/pattern/ split() pattern for -a switch (//'s are optional) + -i[extension] edit <> files in place (makes backup if extension supplied) + -Idirectory specify @INC/#include directory (several -I's allowed) + -l[octal] enable line ending processing, specifies line terminator + -[mM][-]module execute "use/no module..." before executing program + -n assume "while (<>) { ... }" loop around program + -p assume loop like -n but print line also, like sed + -s enable rudimentary parsing for switches after programfile + -S look for programfile using PATH environment variable + -t enable tainting warnings + -T enable tainting checks + -u dump core after parsing program + -U allow unsafe operations + -v print version, patchlevel and license + -V[:variable] print configuration summary (or a single Config.pm variable) + -w enable many useful warnings + -W enable all warnings + -x[directory] ignore text before #!perl line (optionally cd to directory) + -X disable all warnings + +Run 'perldoc perl' for more help with Perl. + + diff --git a/BioScientist/agent_system/toolbase/output/help_docs/help_txt/perl-http-cookies.help.txt b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/perl-http-cookies.help.txt new file mode 100644 index 0000000000000000000000000000000000000000..18a5c4904976b9f3a8bb7d3532dc6a9859dfa854 --- /dev/null +++ b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/perl-http-cookies.help.txt @@ -0,0 +1,36 @@ +$ conda run -n bioenv_perl perl -h +[rc=0] + +Usage: perl [switches] [--] [programfile] [arguments] + -0[octal] specify record separator (\0, if no argument) + -a autosplit mode with -n or -p (splits $_ into @F) + -C[number/list] enables the listed Unicode features + -c check syntax only (runs BEGIN and CHECK blocks) + -d[:debugger] run program under debugger + -D[number/list] set debugging flags (argument is a bit mask or alphabets) + -e program one line of program (several -e's allowed, omit programfile) + -E program like -e, but enables all optional features + -f don't do $sitelib/sitecustomize.pl at startup + -F/pattern/ split() pattern for -a switch (//'s are optional) + -i[extension] edit <> files in place (makes backup if extension supplied) + -Idirectory specify @INC/#include directory (several -I's allowed) + -l[octal] enable line ending processing, specifies line terminator + -[mM][-]module execute "use/no module..." before executing program + -n assume "while (<>) { ... }" loop around program + -p assume loop like -n but print line also, like sed + -s enable rudimentary parsing for switches after programfile + -S look for programfile using PATH environment variable + -t enable tainting warnings + -T enable tainting checks + -u dump core after parsing program + -U allow unsafe operations + -v print version, patchlevel and license + -V[:variable] print configuration summary (or a single Config.pm variable) + -w enable many useful warnings + -W enable all warnings + -x[directory] ignore text before #!perl line (optionally cd to directory) + -X disable all warnings + +Run 'perldoc perl' for more help with Perl. + + diff --git a/BioScientist/agent_system/toolbase/output/help_docs/help_txt/perl-http-message.help.txt b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/perl-http-message.help.txt new file mode 100644 index 0000000000000000000000000000000000000000..18a5c4904976b9f3a8bb7d3532dc6a9859dfa854 --- /dev/null +++ b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/perl-http-message.help.txt @@ -0,0 +1,36 @@ +$ conda run -n bioenv_perl perl -h +[rc=0] + +Usage: perl [switches] [--] [programfile] [arguments] + -0[octal] specify record separator (\0, if no argument) + -a autosplit mode with -n or -p (splits $_ into @F) + -C[number/list] enables the listed Unicode features + -c check syntax only (runs BEGIN and CHECK blocks) + -d[:debugger] run program under debugger + -D[number/list] set debugging flags (argument is a bit mask or alphabets) + -e program one line of program (several -e's allowed, omit programfile) + -E program like -e, but enables all optional features + -f don't do $sitelib/sitecustomize.pl at startup + -F/pattern/ split() pattern for -a switch (//'s are optional) + -i[extension] edit <> files in place (makes backup if extension supplied) + -Idirectory specify @INC/#include directory (several -I's allowed) + -l[octal] enable line ending processing, specifies line terminator + -[mM][-]module execute "use/no module..." before executing program + -n assume "while (<>) { ... }" loop around program + -p assume loop like -n but print line also, like sed + -s enable rudimentary parsing for switches after programfile + -S look for programfile using PATH environment variable + -t enable tainting warnings + -T enable tainting checks + -u dump core after parsing program + -U allow unsafe operations + -v print version, patchlevel and license + -V[:variable] print configuration summary (or a single Config.pm variable) + -w enable many useful warnings + -W enable all warnings + -x[directory] ignore text before #!perl line (optionally cd to directory) + -X disable all warnings + +Run 'perldoc perl' for more help with Perl. + + diff --git a/BioScientist/agent_system/toolbase/output/help_docs/help_txt/perl-image-size.help.txt b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/perl-image-size.help.txt new file mode 100644 index 0000000000000000000000000000000000000000..18a5c4904976b9f3a8bb7d3532dc6a9859dfa854 --- /dev/null +++ b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/perl-image-size.help.txt @@ -0,0 +1,36 @@ +$ conda run -n bioenv_perl perl -h +[rc=0] + +Usage: perl [switches] [--] [programfile] [arguments] + -0[octal] specify record separator (\0, if no argument) + -a autosplit mode with -n or -p (splits $_ into @F) + -C[number/list] enables the listed Unicode features + -c check syntax only (runs BEGIN and CHECK blocks) + -d[:debugger] run program under debugger + -D[number/list] set debugging flags (argument is a bit mask or alphabets) + -e program one line of program (several -e's allowed, omit programfile) + -E program like -e, but enables all optional features + -f don't do $sitelib/sitecustomize.pl at startup + -F/pattern/ split() pattern for -a switch (//'s are optional) + -i[extension] edit <> files in place (makes backup if extension supplied) + -Idirectory specify @INC/#include directory (several -I's allowed) + -l[octal] enable line ending processing, specifies line terminator + -[mM][-]module execute "use/no module..." before executing program + -n assume "while (<>) { ... }" loop around program + -p assume loop like -n but print line also, like sed + -s enable rudimentary parsing for switches after programfile + -S look for programfile using PATH environment variable + -t enable tainting warnings + -T enable tainting checks + -u dump core after parsing program + -U allow unsafe operations + -v print version, patchlevel and license + -V[:variable] print configuration summary (or a single Config.pm variable) + -w enable many useful warnings + -W enable all warnings + -x[directory] ignore text before #!perl line (optionally cd to directory) + -X disable all warnings + +Run 'perldoc perl' for more help with Perl. + + diff --git a/BioScientist/agent_system/toolbase/output/help_docs/help_txt/perl-io-zlib.help.txt b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/perl-io-zlib.help.txt new file mode 100644 index 0000000000000000000000000000000000000000..18a5c4904976b9f3a8bb7d3532dc6a9859dfa854 --- /dev/null +++ b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/perl-io-zlib.help.txt @@ -0,0 +1,36 @@ +$ conda run -n bioenv_perl perl -h +[rc=0] + +Usage: perl [switches] [--] [programfile] [arguments] + -0[octal] specify record separator (\0, if no argument) + -a autosplit mode with -n or -p (splits $_ into @F) + -C[number/list] enables the listed Unicode features + -c check syntax only (runs BEGIN and CHECK blocks) + -d[:debugger] run program under debugger + -D[number/list] set debugging flags (argument is a bit mask or alphabets) + -e program one line of program (several -e's allowed, omit programfile) + -E program like -e, but enables all optional features + -f don't do $sitelib/sitecustomize.pl at startup + -F/pattern/ split() pattern for -a switch (//'s are optional) + -i[extension] edit <> files in place (makes backup if extension supplied) + -Idirectory specify @INC/#include directory (several -I's allowed) + -l[octal] enable line ending processing, specifies line terminator + -[mM][-]module execute "use/no module..." before executing program + -n assume "while (<>) { ... }" loop around program + -p assume loop like -n but print line also, like sed + -s enable rudimentary parsing for switches after programfile + -S look for programfile using PATH environment variable + -t enable tainting warnings + -T enable tainting checks + -u dump core after parsing program + -U allow unsafe operations + -v print version, patchlevel and license + -V[:variable] print configuration summary (or a single Config.pm variable) + -w enable many useful warnings + -W enable all warnings + -x[directory] ignore text before #!perl line (optionally cd to directory) + -X disable all warnings + +Run 'perldoc perl' for more help with Perl. + + diff --git a/BioScientist/agent_system/toolbase/output/help_docs/help_txt/perl-lib.help.txt b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/perl-lib.help.txt new file mode 100644 index 0000000000000000000000000000000000000000..18a5c4904976b9f3a8bb7d3532dc6a9859dfa854 --- /dev/null +++ b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/perl-lib.help.txt @@ -0,0 +1,36 @@ +$ conda run -n bioenv_perl perl -h +[rc=0] + +Usage: perl [switches] [--] [programfile] [arguments] + -0[octal] specify record separator (\0, if no argument) + -a autosplit mode with -n or -p (splits $_ into @F) + -C[number/list] enables the listed Unicode features + -c check syntax only (runs BEGIN and CHECK blocks) + -d[:debugger] run program under debugger + -D[number/list] set debugging flags (argument is a bit mask or alphabets) + -e program one line of program (several -e's allowed, omit programfile) + -E program like -e, but enables all optional features + -f don't do $sitelib/sitecustomize.pl at startup + -F/pattern/ split() pattern for -a switch (//'s are optional) + -i[extension] edit <> files in place (makes backup if extension supplied) + -Idirectory specify @INC/#include directory (several -I's allowed) + -l[octal] enable line ending processing, specifies line terminator + -[mM][-]module execute "use/no module..." before executing program + -n assume "while (<>) { ... }" loop around program + -p assume loop like -n but print line also, like sed + -s enable rudimentary parsing for switches after programfile + -S look for programfile using PATH environment variable + -t enable tainting warnings + -T enable tainting checks + -u dump core after parsing program + -U allow unsafe operations + -v print version, patchlevel and license + -V[:variable] print configuration summary (or a single Config.pm variable) + -w enable many useful warnings + -W enable all warnings + -x[directory] ignore text before #!perl line (optionally cd to directory) + -X disable all warnings + +Run 'perldoc perl' for more help with Perl. + + diff --git a/BioScientist/agent_system/toolbase/output/help_docs/help_txt/perl-mce.help.txt b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/perl-mce.help.txt new file mode 100644 index 0000000000000000000000000000000000000000..18a5c4904976b9f3a8bb7d3532dc6a9859dfa854 --- /dev/null +++ b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/perl-mce.help.txt @@ -0,0 +1,36 @@ +$ conda run -n bioenv_perl perl -h +[rc=0] + +Usage: perl [switches] [--] [programfile] [arguments] + -0[octal] specify record separator (\0, if no argument) + -a autosplit mode with -n or -p (splits $_ into @F) + -C[number/list] enables the listed Unicode features + -c check syntax only (runs BEGIN and CHECK blocks) + -d[:debugger] run program under debugger + -D[number/list] set debugging flags (argument is a bit mask or alphabets) + -e program one line of program (several -e's allowed, omit programfile) + -E program like -e, but enables all optional features + -f don't do $sitelib/sitecustomize.pl at startup + -F/pattern/ split() pattern for -a switch (//'s are optional) + -i[extension] edit <> files in place (makes backup if extension supplied) + -Idirectory specify @INC/#include directory (several -I's allowed) + -l[octal] enable line ending processing, specifies line terminator + -[mM][-]module execute "use/no module..." before executing program + -n assume "while (<>) { ... }" loop around program + -p assume loop like -n but print line also, like sed + -s enable rudimentary parsing for switches after programfile + -S look for programfile using PATH environment variable + -t enable tainting warnings + -T enable tainting checks + -u dump core after parsing program + -U allow unsafe operations + -v print version, patchlevel and license + -V[:variable] print configuration summary (or a single Config.pm variable) + -w enable many useful warnings + -W enable all warnings + -x[directory] ignore text before #!perl line (optionally cd to directory) + -X disable all warnings + +Run 'perldoc perl' for more help with Perl. + + diff --git a/BioScientist/agent_system/toolbase/output/help_docs/help_txt/perl-metabolomics-fragment-annotation.help.txt b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/perl-metabolomics-fragment-annotation.help.txt new file mode 100644 index 0000000000000000000000000000000000000000..755e9fb3160938b710cc6d5fd140fd6106203e93 --- /dev/null +++ b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/perl-metabolomics-fragment-annotation.help.txt @@ -0,0 +1,36 @@ +$ conda run -n bioenv perl -h +[rc=0] + +Usage: perl [switches] [--] [programfile] [arguments] + -0[octal] specify record separator (\0, if no argument) + -a autosplit mode with -n or -p (splits $_ into @F) + -C[number/list] enables the listed Unicode features + -c check syntax only (runs BEGIN and CHECK blocks) + -d[:debugger] run program under debugger + -D[number/list] set debugging flags (argument is a bit mask or alphabets) + -e program one line of program (several -e's allowed, omit programfile) + -E program like -e, but enables all optional features + -f don't do $sitelib/sitecustomize.pl at startup + -F/pattern/ split() pattern for -a switch (//'s are optional) + -i[extension] edit <> files in place (makes backup if extension supplied) + -Idirectory specify @INC/#include directory (several -I's allowed) + -l[octal] enable line ending processing, specifies line terminator + -[mM][-]module execute "use/no module..." before executing program + -n assume "while (<>) { ... }" loop around program + -p assume loop like -n but print line also, like sed + -s enable rudimentary parsing for switches after programfile + -S look for programfile using PATH environment variable + -t enable tainting warnings + -T enable tainting checks + -u dump core after parsing program + -U allow unsafe operations + -v print version, patchlevel and license + -V[:variable] print configuration summary (or a single Config.pm variable) + -w enable many useful warnings + -W enable all warnings + -x[directory] ignore text before #!perl line (optionally cd to directory) + -X disable all warnings + +Run 'perldoc perl' for more help with Perl. + + diff --git a/BioScientist/agent_system/toolbase/output/help_docs/help_txt/perl-number-format.help.txt b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/perl-number-format.help.txt new file mode 100644 index 0000000000000000000000000000000000000000..18a5c4904976b9f3a8bb7d3532dc6a9859dfa854 --- /dev/null +++ b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/perl-number-format.help.txt @@ -0,0 +1,36 @@ +$ conda run -n bioenv_perl perl -h +[rc=0] + +Usage: perl [switches] [--] [programfile] [arguments] + -0[octal] specify record separator (\0, if no argument) + -a autosplit mode with -n or -p (splits $_ into @F) + -C[number/list] enables the listed Unicode features + -c check syntax only (runs BEGIN and CHECK blocks) + -d[:debugger] run program under debugger + -D[number/list] set debugging flags (argument is a bit mask or alphabets) + -e program one line of program (several -e's allowed, omit programfile) + -E program like -e, but enables all optional features + -f don't do $sitelib/sitecustomize.pl at startup + -F/pattern/ split() pattern for -a switch (//'s are optional) + -i[extension] edit <> files in place (makes backup if extension supplied) + -Idirectory specify @INC/#include directory (several -I's allowed) + -l[octal] enable line ending processing, specifies line terminator + -[mM][-]module execute "use/no module..." before executing program + -n assume "while (<>) { ... }" loop around program + -p assume loop like -n but print line also, like sed + -s enable rudimentary parsing for switches after programfile + -S look for programfile using PATH environment variable + -t enable tainting warnings + -T enable tainting checks + -u dump core after parsing program + -U allow unsafe operations + -v print version, patchlevel and license + -V[:variable] print configuration summary (or a single Config.pm variable) + -w enable many useful warnings + -W enable all warnings + -x[directory] ignore text before #!perl line (optionally cd to directory) + -X disable all warnings + +Run 'perldoc perl' for more help with Perl. + + diff --git a/BioScientist/agent_system/toolbase/output/help_docs/help_txt/perl-parent.help.txt b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/perl-parent.help.txt new file mode 100644 index 0000000000000000000000000000000000000000..18a5c4904976b9f3a8bb7d3532dc6a9859dfa854 --- /dev/null +++ b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/perl-parent.help.txt @@ -0,0 +1,36 @@ +$ conda run -n bioenv_perl perl -h +[rc=0] + +Usage: perl [switches] [--] [programfile] [arguments] + -0[octal] specify record separator (\0, if no argument) + -a autosplit mode with -n or -p (splits $_ into @F) + -C[number/list] enables the listed Unicode features + -c check syntax only (runs BEGIN and CHECK blocks) + -d[:debugger] run program under debugger + -D[number/list] set debugging flags (argument is a bit mask or alphabets) + -e program one line of program (several -e's allowed, omit programfile) + -E program like -e, but enables all optional features + -f don't do $sitelib/sitecustomize.pl at startup + -F/pattern/ split() pattern for -a switch (//'s are optional) + -i[extension] edit <> files in place (makes backup if extension supplied) + -Idirectory specify @INC/#include directory (several -I's allowed) + -l[octal] enable line ending processing, specifies line terminator + -[mM][-]module execute "use/no module..." before executing program + -n assume "while (<>) { ... }" loop around program + -p assume loop like -n but print line also, like sed + -s enable rudimentary parsing for switches after programfile + -S look for programfile using PATH environment variable + -t enable tainting warnings + -T enable tainting checks + -u dump core after parsing program + -U allow unsafe operations + -v print version, patchlevel and license + -V[:variable] print configuration summary (or a single Config.pm variable) + -w enable many useful warnings + -W enable all warnings + -x[directory] ignore text before #!perl line (optionally cd to directory) + -X disable all warnings + +Run 'perldoc perl' for more help with Perl. + + diff --git a/BioScientist/agent_system/toolbase/output/help_docs/help_txt/perl-pdf-api2.help.txt b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/perl-pdf-api2.help.txt new file mode 100644 index 0000000000000000000000000000000000000000..18a5c4904976b9f3a8bb7d3532dc6a9859dfa854 --- /dev/null +++ b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/perl-pdf-api2.help.txt @@ -0,0 +1,36 @@ +$ conda run -n bioenv_perl perl -h +[rc=0] + +Usage: perl [switches] [--] [programfile] [arguments] + -0[octal] specify record separator (\0, if no argument) + -a autosplit mode with -n or -p (splits $_ into @F) + -C[number/list] enables the listed Unicode features + -c check syntax only (runs BEGIN and CHECK blocks) + -d[:debugger] run program under debugger + -D[number/list] set debugging flags (argument is a bit mask or alphabets) + -e program one line of program (several -e's allowed, omit programfile) + -E program like -e, but enables all optional features + -f don't do $sitelib/sitecustomize.pl at startup + -F/pattern/ split() pattern for -a switch (//'s are optional) + -i[extension] edit <> files in place (makes backup if extension supplied) + -Idirectory specify @INC/#include directory (several -I's allowed) + -l[octal] enable line ending processing, specifies line terminator + -[mM][-]module execute "use/no module..." before executing program + -n assume "while (<>) { ... }" loop around program + -p assume loop like -n but print line also, like sed + -s enable rudimentary parsing for switches after programfile + -S look for programfile using PATH environment variable + -t enable tainting warnings + -T enable tainting checks + -u dump core after parsing program + -U allow unsafe operations + -v print version, patchlevel and license + -V[:variable] print configuration summary (or a single Config.pm variable) + -w enable many useful warnings + -W enable all warnings + -x[directory] ignore text before #!perl line (optionally cd to directory) + -X disable all warnings + +Run 'perldoc perl' for more help with Perl. + + diff --git a/BioScientist/agent_system/toolbase/output/help_docs/help_txt/perl-pod-escapes.help.txt b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/perl-pod-escapes.help.txt new file mode 100644 index 0000000000000000000000000000000000000000..18a5c4904976b9f3a8bb7d3532dc6a9859dfa854 --- /dev/null +++ b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/perl-pod-escapes.help.txt @@ -0,0 +1,36 @@ +$ conda run -n bioenv_perl perl -h +[rc=0] + +Usage: perl [switches] [--] [programfile] [arguments] + -0[octal] specify record separator (\0, if no argument) + -a autosplit mode with -n or -p (splits $_ into @F) + -C[number/list] enables the listed Unicode features + -c check syntax only (runs BEGIN and CHECK blocks) + -d[:debugger] run program under debugger + -D[number/list] set debugging flags (argument is a bit mask or alphabets) + -e program one line of program (several -e's allowed, omit programfile) + -E program like -e, but enables all optional features + -f don't do $sitelib/sitecustomize.pl at startup + -F/pattern/ split() pattern for -a switch (//'s are optional) + -i[extension] edit <> files in place (makes backup if extension supplied) + -Idirectory specify @INC/#include directory (several -I's allowed) + -l[octal] enable line ending processing, specifies line terminator + -[mM][-]module execute "use/no module..." before executing program + -n assume "while (<>) { ... }" loop around program + -p assume loop like -n but print line also, like sed + -s enable rudimentary parsing for switches after programfile + -S look for programfile using PATH environment variable + -t enable tainting warnings + -T enable tainting checks + -u dump core after parsing program + -U allow unsafe operations + -v print version, patchlevel and license + -V[:variable] print configuration summary (or a single Config.pm variable) + -w enable many useful warnings + -W enable all warnings + -x[directory] ignore text before #!perl line (optionally cd to directory) + -X disable all warnings + +Run 'perldoc perl' for more help with Perl. + + diff --git a/BioScientist/agent_system/toolbase/output/help_docs/help_txt/perl-postscript.help.txt b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/perl-postscript.help.txt new file mode 100644 index 0000000000000000000000000000000000000000..18a5c4904976b9f3a8bb7d3532dc6a9859dfa854 --- /dev/null +++ b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/perl-postscript.help.txt @@ -0,0 +1,36 @@ +$ conda run -n bioenv_perl perl -h +[rc=0] + +Usage: perl [switches] [--] [programfile] [arguments] + -0[octal] specify record separator (\0, if no argument) + -a autosplit mode with -n or -p (splits $_ into @F) + -C[number/list] enables the listed Unicode features + -c check syntax only (runs BEGIN and CHECK blocks) + -d[:debugger] run program under debugger + -D[number/list] set debugging flags (argument is a bit mask or alphabets) + -e program one line of program (several -e's allowed, omit programfile) + -E program like -e, but enables all optional features + -f don't do $sitelib/sitecustomize.pl at startup + -F/pattern/ split() pattern for -a switch (//'s are optional) + -i[extension] edit <> files in place (makes backup if extension supplied) + -Idirectory specify @INC/#include directory (several -I's allowed) + -l[octal] enable line ending processing, specifies line terminator + -[mM][-]module execute "use/no module..." before executing program + -n assume "while (<>) { ... }" loop around program + -p assume loop like -n but print line also, like sed + -s enable rudimentary parsing for switches after programfile + -S look for programfile using PATH environment variable + -t enable tainting warnings + -T enable tainting checks + -u dump core after parsing program + -U allow unsafe operations + -v print version, patchlevel and license + -V[:variable] print configuration summary (or a single Config.pm variable) + -w enable many useful warnings + -W enable all warnings + -x[directory] ignore text before #!perl line (optionally cd to directory) + -X disable all warnings + +Run 'perldoc perl' for more help with Perl. + + diff --git a/BioScientist/agent_system/toolbase/output/help_docs/help_txt/perl-sub-exporter-progressive.help.txt b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/perl-sub-exporter-progressive.help.txt new file mode 100644 index 0000000000000000000000000000000000000000..18a5c4904976b9f3a8bb7d3532dc6a9859dfa854 --- /dev/null +++ b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/perl-sub-exporter-progressive.help.txt @@ -0,0 +1,36 @@ +$ conda run -n bioenv_perl perl -h +[rc=0] + +Usage: perl [switches] [--] [programfile] [arguments] + -0[octal] specify record separator (\0, if no argument) + -a autosplit mode with -n or -p (splits $_ into @F) + -C[number/list] enables the listed Unicode features + -c check syntax only (runs BEGIN and CHECK blocks) + -d[:debugger] run program under debugger + -D[number/list] set debugging flags (argument is a bit mask or alphabets) + -e program one line of program (several -e's allowed, omit programfile) + -E program like -e, but enables all optional features + -f don't do $sitelib/sitecustomize.pl at startup + -F/pattern/ split() pattern for -a switch (//'s are optional) + -i[extension] edit <> files in place (makes backup if extension supplied) + -Idirectory specify @INC/#include directory (several -I's allowed) + -l[octal] enable line ending processing, specifies line terminator + -[mM][-]module execute "use/no module..." before executing program + -n assume "while (<>) { ... }" loop around program + -p assume loop like -n but print line also, like sed + -s enable rudimentary parsing for switches after programfile + -S look for programfile using PATH environment variable + -t enable tainting warnings + -T enable tainting checks + -u dump core after parsing program + -U allow unsafe operations + -v print version, patchlevel and license + -V[:variable] print configuration summary (or a single Config.pm variable) + -w enable many useful warnings + -W enable all warnings + -x[directory] ignore text before #!perl line (optionally cd to directory) + -X disable all warnings + +Run 'perldoc perl' for more help with Perl. + + diff --git a/BioScientist/agent_system/toolbase/output/help_docs/help_txt/perl-sub-exporter.help.txt b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/perl-sub-exporter.help.txt new file mode 100644 index 0000000000000000000000000000000000000000..18a5c4904976b9f3a8bb7d3532dc6a9859dfa854 --- /dev/null +++ b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/perl-sub-exporter.help.txt @@ -0,0 +1,36 @@ +$ conda run -n bioenv_perl perl -h +[rc=0] + +Usage: perl [switches] [--] [programfile] [arguments] + -0[octal] specify record separator (\0, if no argument) + -a autosplit mode with -n or -p (splits $_ into @F) + -C[number/list] enables the listed Unicode features + -c check syntax only (runs BEGIN and CHECK blocks) + -d[:debugger] run program under debugger + -D[number/list] set debugging flags (argument is a bit mask or alphabets) + -e program one line of program (several -e's allowed, omit programfile) + -E program like -e, but enables all optional features + -f don't do $sitelib/sitecustomize.pl at startup + -F/pattern/ split() pattern for -a switch (//'s are optional) + -i[extension] edit <> files in place (makes backup if extension supplied) + -Idirectory specify @INC/#include directory (several -I's allowed) + -l[octal] enable line ending processing, specifies line terminator + -[mM][-]module execute "use/no module..." before executing program + -n assume "while (<>) { ... }" loop around program + -p assume loop like -n but print line also, like sed + -s enable rudimentary parsing for switches after programfile + -S look for programfile using PATH environment variable + -t enable tainting warnings + -T enable tainting checks + -u dump core after parsing program + -U allow unsafe operations + -v print version, patchlevel and license + -V[:variable] print configuration summary (or a single Config.pm variable) + -w enable many useful warnings + -W enable all warnings + -x[directory] ignore text before #!perl line (optionally cd to directory) + -X disable all warnings + +Run 'perldoc perl' for more help with Perl. + + diff --git a/BioScientist/agent_system/toolbase/output/help_docs/help_txt/perl-sub-install.help.txt b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/perl-sub-install.help.txt new file mode 100644 index 0000000000000000000000000000000000000000..18a5c4904976b9f3a8bb7d3532dc6a9859dfa854 --- /dev/null +++ b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/perl-sub-install.help.txt @@ -0,0 +1,36 @@ +$ conda run -n bioenv_perl perl -h +[rc=0] + +Usage: perl [switches] [--] [programfile] [arguments] + -0[octal] specify record separator (\0, if no argument) + -a autosplit mode with -n or -p (splits $_ into @F) + -C[number/list] enables the listed Unicode features + -c check syntax only (runs BEGIN and CHECK blocks) + -d[:debugger] run program under debugger + -D[number/list] set debugging flags (argument is a bit mask or alphabets) + -e program one line of program (several -e's allowed, omit programfile) + -E program like -e, but enables all optional features + -f don't do $sitelib/sitecustomize.pl at startup + -F/pattern/ split() pattern for -a switch (//'s are optional) + -i[extension] edit <> files in place (makes backup if extension supplied) + -Idirectory specify @INC/#include directory (several -I's allowed) + -l[octal] enable line ending processing, specifies line terminator + -[mM][-]module execute "use/no module..." before executing program + -n assume "while (<>) { ... }" loop around program + -p assume loop like -n but print line also, like sed + -s enable rudimentary parsing for switches after programfile + -S look for programfile using PATH environment variable + -t enable tainting warnings + -T enable tainting checks + -u dump core after parsing program + -U allow unsafe operations + -v print version, patchlevel and license + -V[:variable] print configuration summary (or a single Config.pm variable) + -w enable many useful warnings + -W enable all warnings + -x[directory] ignore text before #!perl line (optionally cd to directory) + -X disable all warnings + +Run 'perldoc perl' for more help with Perl. + + diff --git a/BioScientist/agent_system/toolbase/output/help_docs/help_txt/perl-sub-name.help.txt b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/perl-sub-name.help.txt new file mode 100644 index 0000000000000000000000000000000000000000..48ef55e9f0caae28d0971d2140263baec5bada88 --- /dev/null +++ b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/perl-sub-name.help.txt @@ -0,0 +1,8 @@ +$ conda run -n bioenv_perl perldoc sub::name +[rc=13] + +Can't find any loadable formatter class in Pod::Perldoc::Toterm Pod::Perldoc::Toterm Pod::Perldoc::ToTerm Pod::Perldoc::ToTERM Pod::Simple::term Pod::Simple::term Pod::Simple::Term Pod::Simple::TERM Pod::term Pod::term Pod::Term Pod::TERM Pod::Perldoc::Totext Pod::Perldoc::Totext Pod::Perldoc::ToText Pod::Perldoc::ToTEXT Pod::Simple::text Pod::Simple::text Pod::Simple::Text Pod::Simple::TEXT Pod::text Pod::text Pod::Text Pod::TEXT Pod::Perldoc::ToPod?! +Aborting + at /225040511/miniconda3/envs/bioenv_perl/bin/perldoc line 10. + +ERROR conda.cli.main_run:execute(127): `conda run perldoc sub::name` failed. (See above for error) diff --git a/BioScientist/agent_system/toolbase/output/help_docs/help_txt/perl-test-most.help.txt b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/perl-test-most.help.txt new file mode 100644 index 0000000000000000000000000000000000000000..18a5c4904976b9f3a8bb7d3532dc6a9859dfa854 --- /dev/null +++ b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/perl-test-most.help.txt @@ -0,0 +1,36 @@ +$ conda run -n bioenv_perl perl -h +[rc=0] + +Usage: perl [switches] [--] [programfile] [arguments] + -0[octal] specify record separator (\0, if no argument) + -a autosplit mode with -n or -p (splits $_ into @F) + -C[number/list] enables the listed Unicode features + -c check syntax only (runs BEGIN and CHECK blocks) + -d[:debugger] run program under debugger + -D[number/list] set debugging flags (argument is a bit mask or alphabets) + -e program one line of program (several -e's allowed, omit programfile) + -E program like -e, but enables all optional features + -f don't do $sitelib/sitecustomize.pl at startup + -F/pattern/ split() pattern for -a switch (//'s are optional) + -i[extension] edit <> files in place (makes backup if extension supplied) + -Idirectory specify @INC/#include directory (several -I's allowed) + -l[octal] enable line ending processing, specifies line terminator + -[mM][-]module execute "use/no module..." before executing program + -n assume "while (<>) { ... }" loop around program + -p assume loop like -n but print line also, like sed + -s enable rudimentary parsing for switches after programfile + -S look for programfile using PATH environment variable + -t enable tainting warnings + -T enable tainting checks + -u dump core after parsing program + -U allow unsafe operations + -v print version, patchlevel and license + -V[:variable] print configuration summary (or a single Config.pm variable) + -w enable many useful warnings + -W enable all warnings + -x[directory] ignore text before #!perl line (optionally cd to directory) + -X disable all warnings + +Run 'perldoc perl' for more help with Perl. + + diff --git a/BioScientist/agent_system/toolbase/output/help_docs/help_txt/perl-test2-suite.help.txt b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/perl-test2-suite.help.txt new file mode 100644 index 0000000000000000000000000000000000000000..18a5c4904976b9f3a8bb7d3532dc6a9859dfa854 --- /dev/null +++ b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/perl-test2-suite.help.txt @@ -0,0 +1,36 @@ +$ conda run -n bioenv_perl perl -h +[rc=0] + +Usage: perl [switches] [--] [programfile] [arguments] + -0[octal] specify record separator (\0, if no argument) + -a autosplit mode with -n or -p (splits $_ into @F) + -C[number/list] enables the listed Unicode features + -c check syntax only (runs BEGIN and CHECK blocks) + -d[:debugger] run program under debugger + -D[number/list] set debugging flags (argument is a bit mask or alphabets) + -e program one line of program (several -e's allowed, omit programfile) + -E program like -e, but enables all optional features + -f don't do $sitelib/sitecustomize.pl at startup + -F/pattern/ split() pattern for -a switch (//'s are optional) + -i[extension] edit <> files in place (makes backup if extension supplied) + -Idirectory specify @INC/#include directory (several -I's allowed) + -l[octal] enable line ending processing, specifies line terminator + -[mM][-]module execute "use/no module..." before executing program + -n assume "while (<>) { ... }" loop around program + -p assume loop like -n but print line also, like sed + -s enable rudimentary parsing for switches after programfile + -S look for programfile using PATH environment variable + -t enable tainting warnings + -T enable tainting checks + -u dump core after parsing program + -U allow unsafe operations + -v print version, patchlevel and license + -V[:variable] print configuration summary (or a single Config.pm variable) + -w enable many useful warnings + -W enable all warnings + -x[directory] ignore text before #!perl line (optionally cd to directory) + -X disable all warnings + +Run 'perldoc perl' for more help with Perl. + + diff --git a/BioScientist/agent_system/toolbase/output/help_docs/help_txt/perl-text-format.help.txt b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/perl-text-format.help.txt new file mode 100644 index 0000000000000000000000000000000000000000..18a5c4904976b9f3a8bb7d3532dc6a9859dfa854 --- /dev/null +++ b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/perl-text-format.help.txt @@ -0,0 +1,36 @@ +$ conda run -n bioenv_perl perl -h +[rc=0] + +Usage: perl [switches] [--] [programfile] [arguments] + -0[octal] specify record separator (\0, if no argument) + -a autosplit mode with -n or -p (splits $_ into @F) + -C[number/list] enables the listed Unicode features + -c check syntax only (runs BEGIN and CHECK blocks) + -d[:debugger] run program under debugger + -D[number/list] set debugging flags (argument is a bit mask or alphabets) + -e program one line of program (several -e's allowed, omit programfile) + -E program like -e, but enables all optional features + -f don't do $sitelib/sitecustomize.pl at startup + -F/pattern/ split() pattern for -a switch (//'s are optional) + -i[extension] edit <> files in place (makes backup if extension supplied) + -Idirectory specify @INC/#include directory (several -I's allowed) + -l[octal] enable line ending processing, specifies line terminator + -[mM][-]module execute "use/no module..." before executing program + -n assume "while (<>) { ... }" loop around program + -p assume loop like -n but print line also, like sed + -s enable rudimentary parsing for switches after programfile + -S look for programfile using PATH environment variable + -t enable tainting warnings + -T enable tainting checks + -u dump core after parsing program + -U allow unsafe operations + -v print version, patchlevel and license + -V[:variable] print configuration summary (or a single Config.pm variable) + -w enable many useful warnings + -W enable all warnings + -x[directory] ignore text before #!perl line (optionally cd to directory) + -X disable all warnings + +Run 'perldoc perl' for more help with Perl. + + diff --git a/BioScientist/agent_system/toolbase/output/help_docs/help_txt/perl-tie-ixhash.help.txt b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/perl-tie-ixhash.help.txt new file mode 100644 index 0000000000000000000000000000000000000000..18a5c4904976b9f3a8bb7d3532dc6a9859dfa854 --- /dev/null +++ b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/perl-tie-ixhash.help.txt @@ -0,0 +1,36 @@ +$ conda run -n bioenv_perl perl -h +[rc=0] + +Usage: perl [switches] [--] [programfile] [arguments] + -0[octal] specify record separator (\0, if no argument) + -a autosplit mode with -n or -p (splits $_ into @F) + -C[number/list] enables the listed Unicode features + -c check syntax only (runs BEGIN and CHECK blocks) + -d[:debugger] run program under debugger + -D[number/list] set debugging flags (argument is a bit mask or alphabets) + -e program one line of program (several -e's allowed, omit programfile) + -E program like -e, but enables all optional features + -f don't do $sitelib/sitecustomize.pl at startup + -F/pattern/ split() pattern for -a switch (//'s are optional) + -i[extension] edit <> files in place (makes backup if extension supplied) + -Idirectory specify @INC/#include directory (several -I's allowed) + -l[octal] enable line ending processing, specifies line terminator + -[mM][-]module execute "use/no module..." before executing program + -n assume "while (<>) { ... }" loop around program + -p assume loop like -n but print line also, like sed + -s enable rudimentary parsing for switches after programfile + -S look for programfile using PATH environment variable + -t enable tainting warnings + -T enable tainting checks + -u dump core after parsing program + -U allow unsafe operations + -v print version, patchlevel and license + -V[:variable] print configuration summary (or a single Config.pm variable) + -w enable many useful warnings + -W enable all warnings + -x[directory] ignore text before #!perl line (optionally cd to directory) + -X disable all warnings + +Run 'perldoc perl' for more help with Perl. + + diff --git a/BioScientist/agent_system/toolbase/output/help_docs/help_txt/perl-types-serialiser.help.txt b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/perl-types-serialiser.help.txt new file mode 100644 index 0000000000000000000000000000000000000000..18a5c4904976b9f3a8bb7d3532dc6a9859dfa854 --- /dev/null +++ b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/perl-types-serialiser.help.txt @@ -0,0 +1,36 @@ +$ conda run -n bioenv_perl perl -h +[rc=0] + +Usage: perl [switches] [--] [programfile] [arguments] + -0[octal] specify record separator (\0, if no argument) + -a autosplit mode with -n or -p (splits $_ into @F) + -C[number/list] enables the listed Unicode features + -c check syntax only (runs BEGIN and CHECK blocks) + -d[:debugger] run program under debugger + -D[number/list] set debugging flags (argument is a bit mask or alphabets) + -e program one line of program (several -e's allowed, omit programfile) + -E program like -e, but enables all optional features + -f don't do $sitelib/sitecustomize.pl at startup + -F/pattern/ split() pattern for -a switch (//'s are optional) + -i[extension] edit <> files in place (makes backup if extension supplied) + -Idirectory specify @INC/#include directory (several -I's allowed) + -l[octal] enable line ending processing, specifies line terminator + -[mM][-]module execute "use/no module..." before executing program + -n assume "while (<>) { ... }" loop around program + -p assume loop like -n but print line also, like sed + -s enable rudimentary parsing for switches after programfile + -S look for programfile using PATH environment variable + -t enable tainting warnings + -T enable tainting checks + -u dump core after parsing program + -U allow unsafe operations + -v print version, patchlevel and license + -V[:variable] print configuration summary (or a single Config.pm variable) + -w enable many useful warnings + -W enable all warnings + -x[directory] ignore text before #!perl line (optionally cd to directory) + -X disable all warnings + +Run 'perldoc perl' for more help with Perl. + + diff --git a/BioScientist/agent_system/toolbase/output/help_docs/help_txt/perl-xml-sax-writer.help.txt b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/perl-xml-sax-writer.help.txt new file mode 100644 index 0000000000000000000000000000000000000000..18a5c4904976b9f3a8bb7d3532dc6a9859dfa854 --- /dev/null +++ b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/perl-xml-sax-writer.help.txt @@ -0,0 +1,36 @@ +$ conda run -n bioenv_perl perl -h +[rc=0] + +Usage: perl [switches] [--] [programfile] [arguments] + -0[octal] specify record separator (\0, if no argument) + -a autosplit mode with -n or -p (splits $_ into @F) + -C[number/list] enables the listed Unicode features + -c check syntax only (runs BEGIN and CHECK blocks) + -d[:debugger] run program under debugger + -D[number/list] set debugging flags (argument is a bit mask or alphabets) + -e program one line of program (several -e's allowed, omit programfile) + -E program like -e, but enables all optional features + -f don't do $sitelib/sitecustomize.pl at startup + -F/pattern/ split() pattern for -a switch (//'s are optional) + -i[extension] edit <> files in place (makes backup if extension supplied) + -Idirectory specify @INC/#include directory (several -I's allowed) + -l[octal] enable line ending processing, specifies line terminator + -[mM][-]module execute "use/no module..." before executing program + -n assume "while (<>) { ... }" loop around program + -p assume loop like -n but print line also, like sed + -s enable rudimentary parsing for switches after programfile + -S look for programfile using PATH environment variable + -t enable tainting warnings + -T enable tainting checks + -u dump core after parsing program + -U allow unsafe operations + -v print version, patchlevel and license + -V[:variable] print configuration summary (or a single Config.pm variable) + -w enable many useful warnings + -W enable all warnings + -x[directory] ignore text before #!perl line (optionally cd to directory) + -X disable all warnings + +Run 'perldoc perl' for more help with Perl. + + diff --git a/BioScientist/agent_system/toolbase/output/help_docs/help_txt/perl-xml-writer.help.txt b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/perl-xml-writer.help.txt new file mode 100644 index 0000000000000000000000000000000000000000..18a5c4904976b9f3a8bb7d3532dc6a9859dfa854 --- /dev/null +++ b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/perl-xml-writer.help.txt @@ -0,0 +1,36 @@ +$ conda run -n bioenv_perl perl -h +[rc=0] + +Usage: perl [switches] [--] [programfile] [arguments] + -0[octal] specify record separator (\0, if no argument) + -a autosplit mode with -n or -p (splits $_ into @F) + -C[number/list] enables the listed Unicode features + -c check syntax only (runs BEGIN and CHECK blocks) + -d[:debugger] run program under debugger + -D[number/list] set debugging flags (argument is a bit mask or alphabets) + -e program one line of program (several -e's allowed, omit programfile) + -E program like -e, but enables all optional features + -f don't do $sitelib/sitecustomize.pl at startup + -F/pattern/ split() pattern for -a switch (//'s are optional) + -i[extension] edit <> files in place (makes backup if extension supplied) + -Idirectory specify @INC/#include directory (several -I's allowed) + -l[octal] enable line ending processing, specifies line terminator + -[mM][-]module execute "use/no module..." before executing program + -n assume "while (<>) { ... }" loop around program + -p assume loop like -n but print line also, like sed + -s enable rudimentary parsing for switches after programfile + -S look for programfile using PATH environment variable + -t enable tainting warnings + -T enable tainting checks + -u dump core after parsing program + -U allow unsafe operations + -v print version, patchlevel and license + -V[:variable] print configuration summary (or a single Config.pm variable) + -w enable many useful warnings + -W enable all warnings + -x[directory] ignore text before #!perl line (optionally cd to directory) + -X disable all warnings + +Run 'perldoc perl' for more help with Perl. + + diff --git a/BioScientist/agent_system/toolbase/output/help_docs/help_txt/picard.help.txt b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/picard.help.txt new file mode 100644 index 0000000000000000000000000000000000000000..bcdbb8fd26e9fd24df3b88d245b5bc26e455285c --- /dev/null +++ b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/picard.help.txt @@ -0,0 +1,103 @@ +$ conda run -n bioenv_r_bioc picard --help +[rc=1] + +/225040511/miniconda3/envs/bioenv_r_bioc/bin/picard: line 5: warning: setlocale: LC_ALL: cannot change locale (en_US.UTF-8): No such file or directory +USAGE: PicardCommandLine  [-h] + +Available Programs: +-------------------------------------------------------------------------------------- +Base Calling: Tools that process sequencing machine data, e.g. Illumina base calls, and detect sequencing level attributes, e.g. adapters + CheckIlluminaDirectory Asserts the validity for specified Illumina basecalling data.  + CollectIlluminaBasecallingMetrics Collects Illumina Basecalling metrics for a sequencing run.  + CollectIlluminaLaneMetrics Collects Illumina lane metrics for the given BaseCalling analysis directory.  + ExtractIlluminaBarcodes Tool determines the barcode for each read in an Illumina lane.  + IlluminaBasecallsToFastq Generate FASTQ file(s) from Illumina basecall read data.  + IlluminaBasecallsToSam Transforms raw Illumina sequencing data into an unmapped SAM or BAM file. + MarkIlluminaAdapters Reads a SAM or BAM file and rewrites it with new adapter-trimming tags.  + +-------------------------------------------------------------------------------------- +Diagnostics and Quality Control: Tools that collect sequencing quality related and comparative metrics + AccumulateVariantCallingMetrics Combines multiple Variant Calling Metrics files into a single file + BamIndexStats Generate index statistics from a BAM file + CalculateFingerprintMetrics Calculate statistics on fingerprints, checking their viability + CalculateReadGroupChecksum Creates a hash code based on the read groups (RG).  + CheckFingerprint Computes a fingerprint from the supplied input (SAM/BAM or VCF) file and compares it to the provided genotypes + CheckTerminatorBlock Asserts the provided gzip file's (e.g., BAM) last block is well-formed; RC 100 otherwise + ClusterCrosscheckMetrics Clusters the results of a CrosscheckFingerprints run by LOD score + CollectAlignmentSummaryMetrics Produces a summary of alignment metrics from a SAM or BAM file.  + CollectBaseDistributionByCycle Chart the nucleotide distribution per cycle in a SAM or BAM file + CollectGcBiasMetrics Collect metrics regarding GC bias.  + CollectHiSeqXPfFailMetrics Classify PF-Failing reads in a HiSeqX Illumina Basecalling directory into various categories. + CollectHsMetrics Collects hybrid-selection (HS) metrics for a SAM or BAM file.  + CollectIndependentReplicateMetrics **EXPERIMENTAL - USE AT YOUR OWN RISK** Estimates the rate of independent replication of reads within a bam. + CollectInsertSizeMetrics Collect metrics about the insert size distribution of a paired-end library. + CollectJumpingLibraryMetrics Collect jumping library metrics.  + CollectMultipleMetrics Collect multiple classes of metrics. + CollectOxoGMetrics Collect metrics to assess oxidative artifacts. + CollectQualityYieldMetrics Collect metrics about reads that pass quality thresholds and Illumina-specific filters.  + CollectRawWgsMetrics Collect whole genome sequencing-related metrics.  + CollectRnaSeqMetrics Produces RNA alignment metrics for a SAM or BAM file.  + CollectRrbsMetrics Collects metrics from reduced representation bisulfite sequencing (Rrbs) data.  + CollectSamErrorMetrics Program to collect error metrics on bases stratified in various ways. + CollectSequencingArtifactMetrics Collect metrics to quantify single-base sequencing artifacts.  + CollectTargetedPcrMetrics Calculate PCR-related metrics from targeted sequencing data.  + CollectVariantCallingMetrics Collects per-sample and aggregate (spanning all samples) metrics from the provided VCF file + CollectWgsMetrics Collect metrics about coverage and performance of whole genome sequencing (WGS) experiments. + CollectWgsMetricsWithNonZeroCoverage **EXPERIMENTAL - USE AT YOUR OWN RISK** Collect metrics about coverage and performance of whole genome sequencing (WGS) experiments.  + CompareMetrics Compare two metrics files. + CompareSAMs Compare two input ".sam" or ".bam" files.  + ConvertSequencingArtifactToOxoG Extract OxoG metrics from generalized artifacts metrics.  + CrosscheckFingerprints Checks that all data in the input files appear to have come from the same individual + CrosscheckReadGroupFingerprints DEPRECATED: USE CrosscheckFingerprints.  + EstimateLibraryComplexity Estimates the numbers of unique molecules in a sequencing library.  + IdentifyContaminant Computes a fingerprint from the supplied SAM/BAM file, given a contamination estimate. + MeanQualityByCycle Collect mean quality by cycle. + QualityScoreDistribution Chart the distribution of quality scores.  + ValidateSamFile Validates a SAM or BAM file. + ViewSam Prints a SAM or BAM file to the screen + +-------------------------------------------------------------------------------------- +Genotyping Arrays Manipulation: Tools that manipulate data generated by Genotyping arrays + GtcToVcf Program to convert a GTC file to a VCF + MergePedIntoVcf Program to merge a single-sample ped file from zCall into a single-sample VCF. + VcfToAdpc Program to convert an Arrays VCF to an ADPC file. + +-------------------------------------------------------------------------------------- +Intervals Manipulation: Tools that process genomic intervals in various formats + BedToIntervalList Converts a BED file to a Picard Interval List.  + IntervalListToBed Converts an Picard IntervalList file to a BED file. + IntervalListTools A tool for performing various IntervalList manipulations + LiftOverIntervalList Lifts over an interval list from one reference build to another.  + +-------------------------------------------------------------------------------------- +Other: Miscellaneous tools, e.g. those that aid in data streaming + FifoBuffer Provides a large, FIFO buffer that can be used to buffer input and output streams between programs. + +-------------------------------------------------------------------------------------- +Read Data Manipulation: Tools that manipulate read data in SAM, BAM or CRAM format + AddCommentsToBam Adds comments to the header of a BAM file. + AddOATag Record current alignment information to OA tag. + AddOrReplaceReadGroups Assigns all the reads in a file to a single new read-group. + BamToBfq Converts a BAM file into a BFQ (binary fastq formatted) file + BuildBamIndex Generates a BAM index ".bai" file.  + CleanSam Cleans the provided SAM/BAM, soft-clipping beyond-end-of-reference alignments and setting MAPQ to 0 for unmapped reads + DownsampleSam Downsample a SAM or BAM file. + FastqToSam Converts a FASTQ file to an unaligned BAM or SAM file + FilterSamReads Subsets reads from a SAM or BAM file by applying one of several filters. + FixMateInformation Verify mate-pair information between mates and fix if needed. + GatherBamFiles Concatenate efficiently BAM files that resulted from a scattered parallel analysis + MarkDuplicates Identifies duplicate reads.  + MarkDuplicatesWithMateCigar Identifies duplicate reads, accounting for mate CIGAR.  + MergeBamAlignment Merge alignment data from a SAM or BAM with data in an unmapped BAM file.  + MergeSamFiles Merges multiple SAM and/or BAM files into a single file.  + PositionBasedDownsampleSam Downsample a SAM or BAM file to retain a subset of the reads based on the reads location in each tile in the flowcell. + ReorderSam Reorders reads in a SAM or BAM file to match ordering in a second reference file. + ReplaceSamHeader Replaces the SAMFileHeader in a SAM or BAM file.  + RevertOriginalBaseQualitiesAndAddMateCigar Reverts the original base qualities and adds the mate cigar tag to read-group BAMs + RevertSam Reverts SAM or BAM files to a previous state.  + SamFormatConverter Convert a BAM file to a SAM file, or a SAM to a BAM + SamToFastq Converts a SAM or BAM file to FASTQ. + SamToFastqWithTags Converts a SAM or BAM file to FASTQ alongside FASTQs created from tags. + SetNmAndUqTags DEPRECATED: Use SetNmMdAndUqTags instead. + SetNmMdAndUqTags Fixes the NM, MD, and UQ tags in a SAM file  + SimpleMarkDuplicatesWithMateCigar **EXPERIMENTAL - USE AT YOUR OWN RISK** Exami \ No newline at end of file diff --git a/BioScientist/agent_system/toolbase/output/help_docs/help_txt/poa.help.txt b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/poa.help.txt new file mode 100644 index 0000000000000000000000000000000000000000..399d15d29faa8b3d0b905170602ae9354a473036 --- /dev/null +++ b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/poa.help.txt @@ -0,0 +1,9 @@ +$ conda run -n bioenv_cli poa --help +[rc=1] + +black_flag: BAD_DATA poa :v1.0.0 main.c $Revision: 1.2.2.9 $,143 +Error reading matrix file because none specified. +Exiting +end_black_flag + +ERROR conda.cli.main_run:execute(127): `conda run poa --help` failed. (See above for error) diff --git a/BioScientist/agent_system/toolbase/output/help_docs/help_txt/py2bit.help.txt b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/py2bit.help.txt new file mode 100644 index 0000000000000000000000000000000000000000..5fbdaec442ebbf0928b351092c957c05fef8270b --- /dev/null +++ b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/py2bit.help.txt @@ -0,0 +1,6 @@ +$ conda run -n bioenv_cli python -m py2bit --help +[rc=1] + +/225040511/miniconda3/envs/bioenv_cli/bin/python: No code object available for py2bit + +ERROR conda.cli.main_run:execute(127): `conda run python -m py2bit --help` failed. (See above for error) diff --git a/BioScientist/agent_system/toolbase/output/help_docs/help_txt/pyroe.help.txt b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/pyroe.help.txt new file mode 100644 index 0000000000000000000000000000000000000000..aa569d7e88ced4030b3783a7c7edfa06c8c87c84 --- /dev/null +++ b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/pyroe.help.txt @@ -0,0 +1,27 @@ +$ conda run -n bioenv pyroe --help +[rc=0] +usage: pyroe [-h] [-v] + {make-spliced+intronic,make-splici,make-spliced+unspliced,make-spliceu,fetch-quant,id-to-name,convert} + ... + +The pyroe package provides useful functions to support alevin-fry ecosystem. + +options: + -h, --help show this help message and exit + -v, --version show program's version number and exit + +subcommands: + valid subcommands + + {make-spliced+intronic,make-splici,make-spliced+unspliced,make-spliceu,fetch-quant,id-to-name,convert} + additional help + make-spliced+intronic (make-splici) + Make spliced + intronic reference + make-spliced+unspliced (make-spliceu) + Make spliced + unspliced reference + fetch-quant Fetch processed quant results + id-to-name Generate a gene id to gene name mapping file from a + GTF. + convert Convert alevin-fry quantification result to another + format. + diff --git a/BioScientist/agent_system/toolbase/output/help_docs/help_txt/salmon.help.txt b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/salmon.help.txt new file mode 100644 index 0000000000000000000000000000000000000000..0de26f1743ffe42e045178f0005231bc8c977882 --- /dev/null +++ b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/salmon.help.txt @@ -0,0 +1,16 @@ +$ conda run -n bioenv_cli salmon --help +[rc=0] +salmon v1.7.0 + +Usage: salmon -h|--help or + salmon -v|--version or + salmon -c|--cite or + salmon [--no-version-check] [-h | options] + +Commands: + index : create a salmon index + quant : quantify a sample + alevin : single cell analysis + swim : perform super-secret operation + quantmerge : merge multiple quantifications into a single file + diff --git a/BioScientist/agent_system/toolbase/output/help_docs/help_txt/searchgui.help.txt b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/searchgui.help.txt new file mode 100644 index 0000000000000000000000000000000000000000..566dac5084a826d743610cbedbd8d2db0cc2c25a --- /dev/null +++ b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/searchgui.help.txt @@ -0,0 +1,99 @@ +$ conda run -n bioenv_java java -help +[rc=0] + +Usage: java [options] [args...] + (to execute a class) + or java [options] -jar [args...] + (to execute a jar file) + or java [options] -m [/] [args...] + java [options] --module [/] [args...] + (to execute the main class in a module) + or java [options] [args] + (to execute a single source-file program) + + Arguments following the main class, source file, -jar , + -m or --module / are passed as the arguments to + main class. + + where options include: + + -cp + -classpath + --class-path + A : separated list of directories, JAR archives, + and ZIP archives to search for class files. + -p + --module-path ... + A : separated list of directories, each directory + is a directory of modules. + --upgrade-module-path ... + A : separated list of directories, each directory + is a directory of modules that replace upgradeable + modules in the runtime image + --add-modules [,...] + root modules to resolve in addition to the initial module. + can also be ALL-DEFAULT, ALL-SYSTEM, + ALL-MODULE-PATH. + --list-modules + list observable modules and exit + -d + --describe-module + describe a module and exit + --dry-run create VM and load main class but do not execute main method. + The --dry-run option may be useful for validating the + command-line options such as the module system configuration. + --validate-modules + validate all modules and exit + The --validate-modules option may be useful for finding + conflicts and other errors with modules on the module path. + -D= + set a system property + -verbose:[class|module|gc|jni] + enable verbose output + -version print product version to the error stream and exit + --version print product version to the output stream and exit + -showversion print product version to the error stream and continue + --show-version + print product version to the output stream and continue + --show-module-resolution + show module resolution output during startup + -? -h -help + print this help message to the error stream + --help print this help message to the output stream + -X print help on extra options to the error stream + --help-extra print help on extra options to the output stream + -ea[:...|:] + -enableassertions[:...|:] + enable assertions with specified granularity + -da[:...|:] + -disableassertions[:...|:] + disable assertions with specified granularity + -esa | -enablesystemassertions + enable system assertions + -dsa | -disablesystemassertions + disable system assertions + -agentlib:[=] + load native agent library , e.g. -agentlib:jdwp + see also -agentlib:jdwp=help + -agentpath:[=] + load native agent library by full pathname + -javaagent:[=] + load Java programming language agent, see java.lang.instrument + -splash: + show splash screen with specified image + HiDPI scaled images are automatically supported and used + if available. The unscaled image filename, e.g. image.ext, + should always be passed as the argument to the -splash option. + The most appropriate scaled image provided will be picked up + automatically. + See the SplashScreen API documentation for more information + @argument files + one or more argument files containing options + -disable-@files + prevent further argument file expansion + --enable-preview + allow classes to depend on preview features of this release +To specify an argument for a long option, you can use --= or +-- . + + diff --git a/BioScientist/agent_system/toolbase/output/help_docs/help_txt/star-fusion.help.txt b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/star-fusion.help.txt new file mode 100644 index 0000000000000000000000000000000000000000..18a5c4904976b9f3a8bb7d3532dc6a9859dfa854 --- /dev/null +++ b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/star-fusion.help.txt @@ -0,0 +1,36 @@ +$ conda run -n bioenv_perl perl -h +[rc=0] + +Usage: perl [switches] [--] [programfile] [arguments] + -0[octal] specify record separator (\0, if no argument) + -a autosplit mode with -n or -p (splits $_ into @F) + -C[number/list] enables the listed Unicode features + -c check syntax only (runs BEGIN and CHECK blocks) + -d[:debugger] run program under debugger + -D[number/list] set debugging flags (argument is a bit mask or alphabets) + -e program one line of program (several -e's allowed, omit programfile) + -E program like -e, but enables all optional features + -f don't do $sitelib/sitecustomize.pl at startup + -F/pattern/ split() pattern for -a switch (//'s are optional) + -i[extension] edit <> files in place (makes backup if extension supplied) + -Idirectory specify @INC/#include directory (several -I's allowed) + -l[octal] enable line ending processing, specifies line terminator + -[mM][-]module execute "use/no module..." before executing program + -n assume "while (<>) { ... }" loop around program + -p assume loop like -n but print line also, like sed + -s enable rudimentary parsing for switches after programfile + -S look for programfile using PATH environment variable + -t enable tainting warnings + -T enable tainting checks + -u dump core after parsing program + -U allow unsafe operations + -v print version, patchlevel and license + -V[:variable] print configuration summary (or a single Config.pm variable) + -w enable many useful warnings + -W enable all warnings + -x[directory] ignore text before #!perl line (optionally cd to directory) + -X disable all warnings + +Run 'perldoc perl' for more help with Perl. + + diff --git a/BioScientist/agent_system/toolbase/output/help_docs/help_txt/zdb.help.txt b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/zdb.help.txt new file mode 100644 index 0000000000000000000000000000000000000000..71e009b82861481b997f79cb94a451986dd9f801 --- /dev/null +++ b/BioScientist/agent_system/toolbase/output/help_docs/help_txt/zdb.help.txt @@ -0,0 +1,4 @@ +$ conda run -n bioenv_cli zdb --help +[rc=0] +Unkown subcommand --help +