Source code for tooluniverse.metacyc_tool
"""
MetaCyc tool for ToolUniverse.
MetaCyc is a curated database of experimentally elucidated metabolic
pathways from all domains of life.
Website: https://metacyc.org/
BioCyc: https://biocyc.org/
"""
import requests
from typing import Dict, Any, Optional, List
from .base_tool import BaseTool
from .tool_registry import register_tool
# BioCyc API base URL (MetaCyc is part of BioCyc collection)
BIOCYC_BASE_URL = "https://biocyc.org"
[docs]
@register_tool("MetaCycTool")
class MetaCycTool(BaseTool):
"""
Tool for querying MetaCyc metabolic pathway database.
MetaCyc provides:
- Experimentally elucidated metabolic pathways
- Enzymes and reactions
- Metabolites and compounds
- Pathway diagrams
Uses BioCyc web services API.
No authentication required for basic access.
"""
[docs]
def __init__(self, tool_config: Dict[str, Any]):
super().__init__(tool_config)
self.timeout: int = tool_config.get("timeout", 30)
self.parameter = tool_config.get("parameter", {})
[docs]
def run(self, arguments: Dict[str, Any]) -> Dict[str, Any]:
"""Execute MetaCyc query based on operation type."""
operation = arguments.get("operation", "")
if operation == "search_pathways":
return self._search_pathways(arguments)
elif operation == "get_pathway":
return self._get_pathway(arguments)
elif operation == "get_compound":
return self._get_compound(arguments)
elif operation == "get_reaction":
return self._get_reaction(arguments)
else:
return {
"status": "error",
"error": f"Unknown operation: {operation}. Supported: search_pathways, get_pathway, get_compound, get_reaction",
}
[docs]
def _search_pathways(self, arguments: Dict[str, Any]) -> Dict[str, Any]:
"""
Search MetaCyc for pathways.
Args:
arguments: Dict containing:
- query: Search query (pathway name or keyword)
"""
query = arguments.get("query", "")
if not query:
return {"status": "error", "error": "Missing required parameter: query"}
try:
# Use BioCyc quick search API
response = requests.get(
f"{BIOCYC_BASE_URL}/META/search-query",
params={"type": "PATHWAY", "query": query},
timeout=self.timeout,
headers={
"User-Agent": "ToolUniverse/MetaCyc",
"Accept": "application/json",
},
)
# If JSON response works
if "json" in response.headers.get("Content-Type", ""):
data = response.json()
return {
"status": "success",
"data": {
"query": query,
"results": data
if isinstance(data, list)
else data.get("results", []),
},
"metadata": {"source": "MetaCyc"},
}
# Return search guidance with web URL
return {
"status": "success",
"data": {
"query": query,
"results": [],
"search_url": f"{BIOCYC_BASE_URL}/META/substring-search?type=PATHWAY&object={query}",
"note": "Visit search_url for full results. Use get_pathway with pathway ID once found.",
},
"metadata": {"source": "MetaCyc"},
}
except requests.exceptions.RequestException as e:
return {"status": "error", "error": f"Request failed: {str(e)}"}
except Exception as e:
return {"status": "error", "error": f"Unexpected error: {str(e)}"}
[docs]
def _get_pathway(self, arguments: Dict[str, Any]) -> Dict[str, Any]:
"""
Get pathway details by MetaCyc pathway ID.
Args:
arguments: Dict containing:
- pathway_id: MetaCyc pathway ID (e.g., PWY-5177)
"""
pathway_id = arguments.get("pathway_id", "")
if not pathway_id:
return {
"status": "error",
"error": "Missing required parameter: pathway_id",
}
try:
# Try to get pathway data via getxml API
response = requests.get(
f"{BIOCYC_BASE_URL}/getxml",
params={"META": pathway_id},
timeout=self.timeout,
headers={"User-Agent": "ToolUniverse/MetaCyc"},
)
if response.status_code == 200:
# Parse basic info from response
return {
"status": "success",
"data": {
"pathway_id": pathway_id,
"has_data": True,
"url": f"{BIOCYC_BASE_URL}/META/NEW-IMAGE?type=PATHWAY&object={pathway_id}",
"diagram_url": f"{BIOCYC_BASE_URL}/META/NEW-IMAGE?type=PATHWAY&object={pathway_id}&detail-level=2",
},
"metadata": {
"source": "MetaCyc",
"pathway_id": pathway_id,
},
}
return {
"status": "error",
"error": f"Pathway not found: {pathway_id}",
}
except requests.exceptions.RequestException as e:
return {"status": "error", "error": f"Request failed: {str(e)}"}
except Exception as e:
return {"status": "error", "error": f"Unexpected error: {str(e)}"}
[docs]
def _get_compound(self, arguments: Dict[str, Any]) -> Dict[str, Any]:
"""
Get compound details from MetaCyc.
Args:
arguments: Dict containing:
- compound_id: MetaCyc compound ID (e.g., CPD-1)
"""
compound_id = arguments.get("compound_id", "")
if not compound_id:
return {
"status": "error",
"error": "Missing required parameter: compound_id",
}
try:
response = requests.get(
f"{BIOCYC_BASE_URL}/getxml",
params={"META": compound_id},
timeout=self.timeout,
headers={"User-Agent": "ToolUniverse/MetaCyc"},
)
if response.status_code == 200:
return {
"status": "success",
"data": {
"compound_id": compound_id,
"has_data": True,
"url": f"{BIOCYC_BASE_URL}/compound?orgid=META&id={compound_id}",
},
"metadata": {
"source": "MetaCyc",
"compound_id": compound_id,
},
}
return {
"status": "error",
"error": f"Compound not found: {compound_id}",
}
except requests.exceptions.RequestException as e:
return {"status": "error", "error": f"Request failed: {str(e)}"}
except Exception as e:
return {"status": "error", "error": f"Unexpected error: {str(e)}"}
[docs]
def _get_reaction(self, arguments: Dict[str, Any]) -> Dict[str, Any]:
"""
Get reaction details from MetaCyc.
Args:
arguments: Dict containing:
- reaction_id: MetaCyc reaction ID (e.g., RXN-14500)
"""
reaction_id = arguments.get("reaction_id", "")
if not reaction_id:
return {
"status": "error",
"error": "Missing required parameter: reaction_id",
}
try:
response = requests.get(
f"{BIOCYC_BASE_URL}/getxml",
params={"META": reaction_id},
timeout=self.timeout,
headers={"User-Agent": "ToolUniverse/MetaCyc"},
)
if response.status_code == 200:
return {
"status": "success",
"data": {
"reaction_id": reaction_id,
"has_data": True,
"url": f"{BIOCYC_BASE_URL}/META/NEW-IMAGE?type=REACTION&object={reaction_id}",
},
"metadata": {
"source": "MetaCyc",
"reaction_id": reaction_id,
},
}
return {
"status": "error",
"error": f"Reaction not found: {reaction_id}",
}
except requests.exceptions.RequestException as e:
return {"status": "error", "error": f"Request failed: {str(e)}"}
except Exception as e:
return {"status": "error", "error": f"Unexpected error: {str(e)}"}