Source code for tooluniverse.gpcrdb_tool

"""
GPCRdb API tool for ToolUniverse.

GPCRdb is a comprehensive database for G protein-coupled receptors (GPCRs),
which are the targets of ~35% of all approved drugs.

API Documentation: https://docs.gpcrdb.org/web_services.html
No authentication required.
"""

import requests
from typing import Dict, Any, Optional, List
from .base_tool import BaseTool
from .tool_registry import register_tool

# Base URL for GPCRdb API
GPCRDB_API_URL = "https://gpcrdb.org/services"


[docs] @register_tool("GPCRdbTool") class GPCRdbTool(BaseTool): """ Tool for querying GPCRdb GPCR database. GPCRdb provides: - GPCR protein information and classification - Structure data for GPCR crystal/cryo-EM structures - Ligand binding data - Mutation data and effects - Sequence alignments No authentication required. Free public 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 GPCRdb API call based on operation type.""" operation = arguments.get("operation", "") if operation == "get_protein": return self._get_protein(arguments) elif operation == "list_proteins": return self._list_proteins(arguments) elif operation == "get_structures": return self._get_structures(arguments) elif operation == "get_ligands": return self._get_ligands(arguments) elif operation == "get_mutations": return self._get_mutations(arguments) else: return { "status": "error", "error": f"Unknown operation: {operation}. Supported: get_protein, list_proteins, get_structures, get_ligands, get_mutations", }
[docs] def _get_protein(self, arguments: Dict[str, Any]) -> Dict[str, Any]: """ Get detailed protein information for a GPCR. Args: arguments: Dict containing: - protein: Protein entry name (e.g., adrb2_human) or UniProt accession """ protein = arguments.get("protein", "") if not protein: return {"status": "error", "error": "Missing required parameter: protein"} try: response = requests.get( f"{GPCRDB_API_URL}/protein/{protein}/", timeout=self.timeout, headers={ "Accept": "application/json", "User-Agent": "ToolUniverse/GPCRdb", }, ) response.raise_for_status() data = response.json() return { "status": "success", "data": data, "metadata": { "source": "GPCRdb", "protein": protein, }, } except requests.exceptions.HTTPError as e: if e.response.status_code == 404: return {"status": "error", "error": f"Protein not found: {protein}"} return {"status": "error", "error": f"HTTP error: {e.response.status_code}"} 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 _list_proteins(self, arguments: Dict[str, Any]) -> Dict[str, Any]: """ List GPCR protein families from GPCRdb. Args: arguments: Dict containing: - family: GPCR family (e.g., "001" for Class A). If provided, returns proteins in that family. - species: Species (ignored if no family specified) Note: GPCRdb API does not support listing all proteins by species alone. Without family, returns list of protein families. """ family = arguments.get("family", "") arguments.get("species", "human") try: if family: # List proteins in specific family url = f"{GPCRDB_API_URL}/proteinfamily/proteins/{family}/" else: # List protein families (no endpoint for all proteins by species) url = f"{GPCRDB_API_URL}/proteinfamily/" response = requests.get( url, timeout=self.timeout, headers={ "Accept": "application/json", "User-Agent": "ToolUniverse/GPCRdb", }, ) response.raise_for_status() data = response.json() proteins = data if isinstance(data, list) else [data] return { "status": "success", "data": { "proteins": proteins, "count": len(proteins), "family": family if family else "all families", "note": "Specify 'family' parameter to list proteins in a specific family. Without family, returns list of protein families.", }, "metadata": { "source": "GPCRdb", }, } 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_structures(self, arguments: Dict[str, Any]) -> Dict[str, Any]: """ Get GPCR structure information. Args: arguments: Dict containing: - protein: Protein entry name (optional - if not provided, returns all structures) - state: Receptor state filter (active, inactive, intermediate) """ protein = arguments.get("protein", "") state = arguments.get("state", "") try: if protein: url = f"{GPCRDB_API_URL}/structure/protein/{protein}/" else: url = f"{GPCRDB_API_URL}/structure/" response = requests.get( url, timeout=self.timeout, headers={ "Accept": "application/json", "User-Agent": "ToolUniverse/GPCRdb", }, ) response.raise_for_status() data = response.json() structures = data if isinstance(data, list) else [data] # Filter by state if specified if state: structures = [ s for s in structures if s.get("state", "").lower() == state.lower() ] return { "status": "success", "data": { "structures": structures, "count": len(structures), "protein": protein if protein else "all", "state_filter": state if state else "all", }, "metadata": { "source": "GPCRdb", }, } except requests.exceptions.HTTPError as e: if e.response.status_code == 404: return { "status": "success", "data": {"structures": [], "count": 0}, "metadata": {"note": "No structures found"}, } return {"status": "error", "error": f"HTTP error: {e.response.status_code}"} 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_ligands(self, arguments: Dict[str, Any]) -> Dict[str, Any]: """ Get ligands associated with a GPCR. Args: arguments: Dict containing: - protein: Protein entry name (e.g., adrb2_human) """ protein = arguments.get("protein", "") if not protein: return {"status": "error", "error": "Missing required parameter: protein"} try: response = requests.get( f"{GPCRDB_API_URL}/ligands/protein/{protein}/", timeout=self.timeout, headers={ "Accept": "application/json", "User-Agent": "ToolUniverse/GPCRdb", }, ) response.raise_for_status() data = response.json() ligands = data if isinstance(data, list) else data.get("ligands", []) return { "status": "success", "data": { "protein": protein, "ligands": ligands, "count": len(ligands), }, "metadata": { "source": "GPCRdb", "protein": protein, }, } except requests.exceptions.HTTPError as e: if e.response.status_code == 404: return { "status": "success", "data": {"protein": protein, "ligands": [], "count": 0}, "metadata": {"note": "No ligands found for this protein"}, } return {"status": "error", "error": f"HTTP error: {e.response.status_code}"} 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_mutations(self, arguments: Dict[str, Any]) -> Dict[str, Any]: """ Get mutation data for a GPCR. Args: arguments: Dict containing: - protein: Protein entry name (e.g., adrb2_human) """ protein = arguments.get("protein", "") if not protein: return {"status": "error", "error": "Missing required parameter: protein"} try: response = requests.get( f"{GPCRDB_API_URL}/mutants/protein/{protein}/", timeout=self.timeout, headers={ "Accept": "application/json", "User-Agent": "ToolUniverse/GPCRdb", }, ) response.raise_for_status() data = response.json() mutations = data if isinstance(data, list) else data.get("mutations", []) return { "status": "success", "data": { "protein": protein, "mutations": mutations, "count": len(mutations), }, "metadata": { "source": "GPCRdb", "protein": protein, }, } except requests.exceptions.HTTPError as e: if e.response.status_code == 404: return { "status": "success", "data": {"protein": protein, "mutations": [], "count": 0}, "metadata": {"note": "No mutation data found"}, } return {"status": "error", "error": f"HTTP error: {e.response.status_code}"} 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)}"}