diff --git a/crewai_tools/__init__.py b/crewai_tools/__init__.py index 05482ae7..2dcc465c 100644 --- a/crewai_tools/__init__.py +++ b/crewai_tools/__init__.py @@ -34,6 +34,7 @@ FirecrawlCrawlWebsiteTool, FirecrawlScrapeWebsiteTool, FirecrawlSearchTool, + GibsonAIQueryTool, GithubSearchTool, HyperbrowserLoadTool, JSONSearchTool, diff --git a/crewai_tools/tools/__init__.py b/crewai_tools/tools/__init__.py index 05219c4f..2865ee8b 100644 --- a/crewai_tools/tools/__init__.py +++ b/crewai_tools/tools/__init__.py @@ -25,6 +25,7 @@ FirecrawlScrapeWebsiteTool, ) from .firecrawl_search_tool.firecrawl_search_tool import FirecrawlSearchTool +from .gibsonai_query_tool.gibsonai_query_tool import GibsonAIQueryTool from .github_search_tool.github_search_tool import GithubSearchTool from .hyperbrowser_load_tool.hyperbrowser_load_tool import HyperbrowserLoadTool from .json_search_tool.json_search_tool import JSONSearchTool @@ -105,6 +106,6 @@ from .brightdata_tool import ( BrightDataDatasetTool, BrightDataSearchTool, - BrightDataWebUnlockerTool + BrightDataWebUnlockerTool, ) from .zapier_action_tool.zapier_action_tool import ZapierActionTools diff --git a/crewai_tools/tools/gibsonai_query_tool/README.md b/crewai_tools/tools/gibsonai_query_tool/README.md new file mode 100644 index 00000000..bebaf7f1 --- /dev/null +++ b/crewai_tools/tools/gibsonai_query_tool/README.md @@ -0,0 +1,176 @@ +# GibsonAIQueryTool + +## Description +This tool enables direct SQL query execution against [GibsonAI](https://www.gibsonai.com/) serverless databases (MySQL/PostgreSQL) via the hosted Data API. The GibsonAIQueryTool provides a flexible interface for performing database operations including SELECT queries for data retrieval and INSERT/UPDATE/DELETE operations for data modification. It works with any database schema deployed in your GibsonAI project, making it a versatile tool for database interactions. + +## Installation +To install the `crewai_tools` package and utilize the GibsonAIQueryTool, execute the following command in your terminal: + +```shell +pip install 'crewai[tools]' +``` + +## Setup + +### 1. Set up your GibsonAI Project +In [GibsonAI](https://app.gibsonai.com), create a new project with your desired database schema. For example: + +```txt +I want to create a customer management system with the following tables: +- "customers" table with fields (name, email, phone, created_at) +- "orders" table with fields (customer_id, product_name, quantity, price, order_date) +- "products" table with fields (name, description, price, stock_quantity) +``` + +Once generated, click `Deploy` to Production and copy the API key from the `Data API` tab. + +### 2. Set up Environment Variables +Create a `.env` file in your project root and add your GibsonAI API key: + +```env +GIBSONAI_API_KEY=your_project_api_key_here +``` + +## Example Usage + +```python +from crewai_tools import GibsonAIQueryTool + +# Initialize the tool +tool = GibsonAIQueryTool() + +# Or initialize with explicit API key +tool = GibsonAIQueryTool(api_key="your_api_key_here") + +# Example queries +# 1. Select data +result = tool.run(query="SELECT * FROM customers WHERE email LIKE '%@gmail.com'") + +# 2. Insert new data +result = tool.run(query="INSERT INTO customers (name, email, phone) VALUES ('John Doe', 'john@example.com', '555-0123')") + +# 3. Update existing data +result = tool.run(query="UPDATE products SET price = 29.99 WHERE name = 'Widget'") + +# 4. Delete data +result = tool.run(query="DELETE FROM orders WHERE order_date < '2023-01-01'") + +# 5. Parameterized query (recommended for security) +result = tool.run( + query="SELECT * FROM customers WHERE name = ? AND email = ?", + parameters={"name": "John Doe", "email": "john@example.com"} +) +``` + +## Arguments +The GibsonAIQueryTool accepts the following arguments: + +- `query` (required): A string containing the SQL query to execute. This can be any valid SQL statement (SELECT, INSERT, UPDATE, DELETE) +- `parameters` (optional): A dictionary containing parameters for parameterized queries. Use this for safer query execution to prevent SQL injection attacks. + +## Supported Operations + +### Data Retrieval (SELECT) +```python +# Simple select +tool.run(query="SELECT * FROM customers") + +# Complex select with joins +tool.run(query=""" + SELECT c.name, c.email, COUNT(o.id) as order_count + FROM customers c + LEFT JOIN orders o ON c.id = o.customer_id + GROUP BY c.id, c.name, c.email +""") +``` + +### Data Modification (INSERT, UPDATE, DELETE) +```python +# Insert single record +tool.run(query="INSERT INTO products (name, description, price) VALUES ('New Product', 'Description', 19.99)") + +# Update records +tool.run(query="UPDATE customers SET phone = '555-9999' WHERE email = 'john@example.com'") + +# Delete records +tool.run(query="DELETE FROM orders WHERE quantity = 0") +``` + +### Parameterized Queries (Recommended) +```python +# Safe parameter substitution +tool.run( + query="SELECT * FROM customers WHERE created_at BETWEEN ? AND ?", + parameters={"start_date": "2023-01-01", "end_date": "2023-12-31"} +) + +tool.run( + query="INSERT INTO customers (name, email, phone) VALUES (?, ?, ?)", + parameters={"name": "Jane Smith", "email": "jane@example.com", "phone": "555-0456"} +) +``` + +## Response Format +The tool returns formatted responses based on the query type: + +- **SELECT queries**: Returns row count and formatted data +- **INSERT/UPDATE/DELETE**: Returns success message with affected row count +- **Errors**: Returns detailed error messages for troubleshooting + +## Security Best Practices + +1. **Use parameterized queries** when dealing with user input to prevent SQL injection +2. **Store your API key securely** in environment variables, not in code +3. **Limit database permissions** in your GibsonAI project to only what's needed +4. **Validate input data** before constructing queries +5. **Use transactions** for multi-step operations when supported + +## Error Handling +The tool provides comprehensive error handling for: +- Invalid SQL syntax +- Authentication failures +- Network connectivity issues +- Database constraint violations +- Timeout errors + +## Integration with CrewAI Agents +This tool is designed to work seamlessly with CrewAI agents for database-driven workflows: + +```python +from crewai import Agent, Task, Crew +from crewai_tools import GibsonAIQueryTool + +# Create database agent +db_agent = Agent( + role='Database Analyst', + goal='Query and analyze customer data', + backstory='Expert in SQL and data analysis', + tools=[GibsonAIQueryTool()], + verbose=True +) + +# Create analysis task +analysis_task = Task( + description='Find the top 10 customers by order value', + agent=db_agent, + expected_output='List of top customers with their total order values' +) + +# Execute the crew +crew = Crew(agents=[db_agent], tasks=[analysis_task]) +result = crew.kickoff() +``` + +## Troubleshooting + +### Common Issues +1. **"Missing GIBSONAI_API_KEY"**: Ensure your API key is set in the environment or passed to the constructor +2. **"Unauthorized"**: Verify your API key is correct and has the necessary permissions +3. **"Invalid query"**: Check your SQL syntax and ensure it matches your schema +4. **"Connection failed"**: Check your internet connection and GibsonAI service status + +### Getting Help +- Check your GibsonAI project's Data API documentation +- Verify your database schema in the GibsonAI console +- Test simple queries first before complex operations +- Use parameterized queries for dynamic content diff --git a/crewai_tools/tools/gibsonai_query_tool/__init__.py b/crewai_tools/tools/gibsonai_query_tool/__init__.py new file mode 100644 index 00000000..311038ff --- /dev/null +++ b/crewai_tools/tools/gibsonai_query_tool/__init__.py @@ -0,0 +1,3 @@ +from .gibsonai_query_tool import GibsonAIQueryTool + +__all__ = ["GibsonAIQueryTool"] diff --git a/crewai_tools/tools/gibsonai_query_tool/gibsonai_query_tool.py b/crewai_tools/tools/gibsonai_query_tool/gibsonai_query_tool.py new file mode 100644 index 00000000..32e47f73 --- /dev/null +++ b/crewai_tools/tools/gibsonai_query_tool/gibsonai_query_tool.py @@ -0,0 +1,210 @@ +import json +import os +from typing import Type, Optional, Any, Dict, List + +import requests +from dotenv import load_dotenv +from pydantic import BaseModel, Field + +from crewai.tools import BaseTool, EnvVar + +# Load environment variables from .env file +load_dotenv() + + +class GibsonAIQueryInput(BaseModel): + """Input schema for GibsonAIQueryTool.""" + + query: str = Field( + description="SQL query to execute against the GibsonAI database. " + "This should be a properly formatted SQL query (SELECT, INSERT, UPDATE, DELETE) " + "that will be executed against your GibsonAI project database schema." + ) + parameters: Optional[Dict[str, Any]] = Field( + default=None, + description="Optional parameters for parameterized queries. " + "Use this for prepared statements to avoid SQL injection.", + ) + + +class GibsonAIQueryTool(BaseTool): + """ + Tool for executing SQL queries against GibsonAI database via hosted Data API. + + This tool allows you to execute SQL queries against your GibsonAI project database, + supporting data operations including SELECT, INSERT, UPDATE, and DELETE. + It works with any database schema deployed in your GibsonAI project. + """ + + name: str = "GibsonAIQueryTool" + description: str = """ + Executes SQL queries against a GibsonAI database using the hosted Data API. + + This tool supports: + - SELECT queries for data retrieval + - INSERT queries for adding new data + - UPDATE queries for modifying existing data + - DELETE queries for removing data + - Parameterized queries for security + + The query should be properly formatted SQL that matches your GibsonAI project's schema. + For parameterized queries, use the 'parameters' field to pass values safely. + + Example queries: + - "SELECT * FROM users WHERE status = 'active'" + - "INSERT INTO contacts (name, email) VALUES ('John Doe', 'john@example.com')" + - "UPDATE products SET price = 99.99 WHERE id = 1" + - "DELETE FROM orders WHERE created_at < '2023-01-01'" + """ + args_schema: Type[BaseModel] = GibsonAIQueryInput + env_vars: List[EnvVar] = [ + EnvVar( + name="GIBSONAI_API_KEY", + description="API key for GibsonAI Data API", + required=True, + ), + ] + + def __init__(self, api_key: Optional[str] = None, **kwargs): + super().__init__(**kwargs) + # Initialize API configuration + self._api_base_url = "https://api.gibsonai.com/v1/-" + self._api_key = api_key or os.getenv("GIBSONAI_API_KEY") + + if not self._api_key: + raise ValueError( + "Missing GIBSONAI_API_KEY. Please provide it as a parameter " + "or set the GIBSONAI_API_KEY environment variable." + ) + + def _run(self, query: str, parameters: Optional[Dict[str, Any]] = None) -> str: + """ + Execute the SQL query against the GibsonAI database. + + Args: + query: SQL query string to execute + parameters: Optional parameters for parameterized queries + + Returns: + String containing the query results or success/error message + """ + try: + # Prepare the request payload + payload = {"query": query.strip()} + + if parameters: + payload["parameters"] = parameters + + # Execute the query via GibsonAI Data API + response = requests.post( + f"{self._api_base_url}/query", + json=payload, + headers={ + "X-Gibson-API-Key": self._api_key, + "Content-Type": "application/json", + }, + timeout=30, + ) + + # Handle different response status codes + if response.status_code == 200: + result = response.json() + return self._format_response(query, result) + elif response.status_code == 400: + error_detail = response.json().get("detail", "Bad request") + return f"Error: Invalid query - {error_detail}" + elif response.status_code == 401: + return "Error: Unauthorized - Please check your GIBSONAI_API_KEY" + elif response.status_code == 403: + return "Error: Forbidden - Insufficient permissions for this operation" + elif response.status_code == 404: + return "Error: Not found - Check your API endpoint or project configuration" + elif response.status_code == 500: + return "Error: Internal server error - Please try again later" + else: + return f"Error: Unexpected response (Status {response.status_code}): {response.text}" + + except Exception as e: + # Handle specific request exceptions + if "Timeout" in str(type(e).__name__): + return "Error: Request timeout - The query took too long to execute" + elif "ConnectionError" in str(type(e).__name__): + return "Error: Connection failed - Unable to reach GibsonAI API" + elif "RequestException" in str(type(e).__name__): + return f"Error: Request failed - {str(e)}" + elif "JSONDecodeError" in str(type(e).__name__): + return "Error: Invalid JSON response from API" + else: + return f"Error: Unexpected error occurred - {str(e)}" + + def _format_response(self, query: str, result: Dict[str, Any]) -> str: + """ + Format the API response into a readable string. + + Args: + query: The original SQL query + result: The response data from the API + + Returns: + Formatted string representation of the results + """ + try: + query_type = query.strip().upper().split()[0] + + # Handle different types of query responses + if query_type == "SELECT": + return self._format_select_response(result) + elif query_type in ["INSERT", "UPDATE", "DELETE"]: + return self._format_modification_response(query_type, result) + else: + return self._format_generic_response(result) + + except Exception: + # Fallback to generic formatting if parsing fails + return f"Query executed successfully. Raw response: {json.dumps(result, indent=2)}" + + def _format_select_response(self, result: Dict[str, Any]) -> str: + """Format SELECT query response.""" + if "data" in result: + data = result["data"] + if isinstance(data, list): + if len(data) == 0: + return "Query executed successfully. No rows returned." + + # Format as a table-like structure + rows_text = [] + for i, row in enumerate(data): + if isinstance(row, dict): + row_items = [f"{k}: {v}" for k, v in row.items()] + rows_text.append(f"Row {i + 1}: {{{', '.join(row_items)}}}") + else: + rows_text.append(f"Row {i + 1}: {row}") + + return ( + f"Query executed successfully. Retrieved {len(data)} row(s):\n" + + "\n".join(rows_text) + ) + else: + return f"Query executed successfully. Result: {data}" + + return f"Query executed successfully. Response: {json.dumps(result, indent=2)}" + + def _format_modification_response( + self, query_type: str, result: Dict[str, Any] + ) -> str: + """Format INSERT, UPDATE, DELETE query response.""" + affected_rows = result.get( + "affected_rows", result.get("rowsAffected", "unknown") + ) + + if affected_rows != "unknown": + return f"{query_type} query executed successfully. {affected_rows} row(s) affected." + + return f"{query_type} query executed successfully." + + def _format_generic_response(self, result: Dict[str, Any]) -> str: + """Format generic query response.""" + if "success" in result and result["success"]: + return "Query executed successfully." + + return f"Query executed. Response: {json.dumps(result, indent=2)}" diff --git a/tests/tools/test_gibsonai_query_tool.py b/tests/tools/test_gibsonai_query_tool.py new file mode 100644 index 00000000..24e01a75 --- /dev/null +++ b/tests/tools/test_gibsonai_query_tool.py @@ -0,0 +1,305 @@ +import json +from unittest.mock import MagicMock, patch + +import pytest +import requests + +from crewai_tools.tools.gibsonai_query_tool.gibsonai_query_tool import GibsonAIQueryTool + + +@pytest.fixture +def mock_requests(): + with patch( + "crewai_tools.tools.gibsonai_query_tool.gibsonai_query_tool.requests" + ) as mock: + yield mock + + +@pytest.fixture +def tool(): + return GibsonAIQueryTool(api_key="test_api_key") + + +def test_tool_initialization(): + """Test tool initialization with API key.""" + tool = GibsonAIQueryTool(api_key="test_key") + assert tool._api_key == "test_key" + assert tool._api_base_url == "https://api.gibsonai.com/v1/-" + + +def test_tool_initialization_without_api_key(): + """Test tool initialization without API key raises ValueError.""" + with patch.dict("os.environ", {}, clear=True): + with pytest.raises(ValueError, match="Missing GIBSONAI_API_KEY"): + GibsonAIQueryTool() + + +def test_tool_initialization_with_env_var(): + """Test tool initialization using environment variable.""" + with patch.dict("os.environ", {"GIBSONAI_API_KEY": "env_key"}): + tool = GibsonAIQueryTool() + assert tool._api_key == "env_key" + + +def test_successful_select_query(tool, mock_requests): + """Test successful SELECT query execution.""" + mock_response = MagicMock() + mock_response.status_code = 200 + mock_response.json.return_value = { + "data": [ + {"id": 1, "name": "John Doe", "email": "john@example.com"}, + {"id": 2, "name": "Jane Smith", "email": "jane@example.com"}, + ] + } + mock_requests.post.return_value = mock_response + + result = tool._run(query="SELECT * FROM users") + + assert "Query executed successfully" in result + assert "Retrieved 2 row(s)" in result + assert "John Doe" in result + assert "Jane Smith" in result + + mock_requests.post.assert_called_once_with( + "https://api.gibsonai.com/v1/-/query", + json={"query": "SELECT * FROM users"}, + headers={ + "X-Gibson-API-Key": "test_api_key", + "Content-Type": "application/json", + }, + timeout=30, + ) + + +def test_successful_insert_query(tool, mock_requests): + """Test successful INSERT query execution.""" + mock_response = MagicMock() + mock_response.status_code = 200 + mock_response.json.return_value = {"affected_rows": 1} + mock_requests.post.return_value = mock_response + + result = tool._run( + query="INSERT INTO users (name, email) VALUES ('Test User', 'test@example.com')" + ) + + assert "INSERT query executed successfully" in result + assert "1 row(s) affected" in result + + +def test_successful_update_query(tool, mock_requests): + """Test successful UPDATE query execution.""" + mock_response = MagicMock() + mock_response.status_code = 200 + mock_response.json.return_value = {"rowsAffected": 3} + mock_requests.post.return_value = mock_response + + result = tool._run( + query="UPDATE users SET status = 'active' WHERE created_at > '2023-01-01'" + ) + + assert "UPDATE query executed successfully" in result + assert "3 row(s) affected" in result + + +def test_successful_delete_query(tool, mock_requests): + """Test successful DELETE query execution.""" + mock_response = MagicMock() + mock_response.status_code = 200 + mock_response.json.return_value = {"affected_rows": 5} + mock_requests.post.return_value = mock_response + + result = tool._run(query="DELETE FROM old_records WHERE created_at < '2020-01-01'") + + assert "DELETE query executed successfully" in result + assert "5 row(s) affected" in result + + +def test_parameterized_query(tool, mock_requests): + """Test parameterized query execution.""" + mock_response = MagicMock() + mock_response.status_code = 200 + mock_response.json.return_value = {"data": [{"id": 1, "name": "John Doe"}]} + mock_requests.post.return_value = mock_response + + parameters = {"name": "John Doe", "email": "john@example.com"} + result = tool._run( + query="SELECT * FROM users WHERE name = ? AND email = ?", parameters=parameters + ) + + assert "Query executed successfully" in result + mock_requests.post.assert_called_once_with( + "https://api.gibsonai.com/v1/-/query", + json={ + "query": "SELECT * FROM users WHERE name = ? AND email = ?", + "parameters": parameters, + }, + headers={ + "X-Gibson-API-Key": "test_api_key", + "Content-Type": "application/json", + }, + timeout=30, + ) + + +def test_empty_select_result(tool, mock_requests): + """Test SELECT query with no results.""" + mock_response = MagicMock() + mock_response.status_code = 200 + mock_response.json.return_value = {"data": []} + mock_requests.post.return_value = mock_response + + result = tool._run(query="SELECT * FROM users WHERE id = 999") + + assert "Query executed successfully" in result + assert "No rows returned" in result + + +def test_bad_request_error(tool, mock_requests): + """Test handling of 400 Bad Request error.""" + mock_response = MagicMock() + mock_response.status_code = 400 + mock_response.json.return_value = {"detail": "Invalid SQL syntax"} + mock_requests.post.return_value = mock_response + + result = tool._run(query="INVALID SQL QUERY") + + assert "Error: Invalid query" in result + assert "Invalid SQL syntax" in result + + +def test_unauthorized_error(tool, mock_requests): + """Test handling of 401 Unauthorized error.""" + mock_response = MagicMock() + mock_response.status_code = 401 + mock_requests.post.return_value = mock_response + + result = tool._run(query="SELECT * FROM users") + + assert "Error: Unauthorized" in result + assert "check your GIBSONAI_API_KEY" in result + + +def test_forbidden_error(tool, mock_requests): + """Test handling of 403 Forbidden error.""" + mock_response = MagicMock() + mock_response.status_code = 403 + mock_requests.post.return_value = mock_response + + result = tool._run(query="DROP TABLE important_data") + + assert "Error: Forbidden" in result + assert "Insufficient permissions" in result + + +def test_not_found_error(tool, mock_requests): + """Test handling of 404 Not Found error.""" + mock_response = MagicMock() + mock_response.status_code = 404 + mock_requests.post.return_value = mock_response + + result = tool._run(query="SELECT * FROM non_existent_table") + + assert "Error: Not found" in result + + +def test_server_error(tool, mock_requests): + """Test handling of 500 Internal Server Error.""" + mock_response = MagicMock() + mock_response.status_code = 500 + mock_requests.post.return_value = mock_response + + result = tool._run(query="SELECT * FROM users") + + assert "Error: Internal server error" in result + assert "try again later" in result + + +def test_unexpected_status_code(tool, mock_requests): + """Test handling of unexpected status codes.""" + mock_response = MagicMock() + mock_response.status_code = 418 + mock_response.text = "I'm a teapot" + mock_requests.post.return_value = mock_response + + result = tool._run(query="SELECT * FROM users") + + assert "Error: Unexpected response (Status 418)" in result + assert "I'm a teapot" in result + + +def test_timeout_error(tool, mock_requests): + """Test handling of timeout errors.""" + mock_requests.post.side_effect = requests.exceptions.Timeout() + + result = tool._run(query="SELECT * FROM large_table") + + assert "Error: Request timeout" in result + assert "took too long to execute" in result + + +def test_connection_error(tool, mock_requests): + """Test handling of connection errors.""" + mock_requests.post.side_effect = requests.exceptions.ConnectionError() + + result = tool._run(query="SELECT * FROM users") + + assert "Error: Connection failed" in result + assert "Unable to reach GibsonAI API" in result + + +def test_request_exception(tool, mock_requests): + """Test handling of general request exceptions.""" + mock_requests.post.side_effect = requests.exceptions.RequestException( + "Network error" + ) + + result = tool._run(query="SELECT * FROM users") + + assert "Error: Request failed" in result + assert "Network error" in result + + +def test_json_decode_error(tool, mock_requests): + """Test handling of invalid JSON responses.""" + mock_response = MagicMock() + mock_response.status_code = 200 + mock_response.json.side_effect = json.JSONDecodeError("Invalid JSON", "", 0) + mock_requests.post.return_value = mock_response + + result = tool._run(query="SELECT * FROM users") + + assert "Error: Invalid JSON response from API" in result + + +def test_unexpected_exception(tool, mock_requests): + """Test handling of unexpected exceptions.""" + mock_requests.post.side_effect = Exception("Unexpected error") + + result = tool._run(query="SELECT * FROM users") + + assert "Error: Unexpected error occurred" in result + assert "Unexpected error" in result + + +def test_query_whitespace_handling(tool, mock_requests): + """Test that query whitespace is properly handled.""" + mock_response = MagicMock() + mock_response.status_code = 200 + mock_response.json.return_value = {"data": []} + mock_requests.post.return_value = mock_response + + tool._run(query=" SELECT * FROM users ") + + mock_requests.post.assert_called_once() + call_args = mock_requests.post.call_args + assert call_args[1]["json"]["query"] == "SELECT * FROM users" + + +def test_format_response_fallback(tool): + """Test format response fallback when parsing fails.""" + result_data = {"unexpected": "format", "data": "value"} + + formatted = tool._format_response("SELECT * FROM users", result_data) + + assert "Query executed successfully" in formatted + assert "Result: value" in formatted diff --git a/tool.specs.json b/tool.specs.json index c940d4b9..650b7f31 100644 --- a/tool.specs.json +++ b/tool.specs.json @@ -798,6 +798,16 @@ ], "title": "Bucket Name" }, + "cluster": { + "anyOf": [ + {}, + { + "type": "null" + } + ], + "default": null, + "title": "Cluster" + }, "collection_name": { "anyOf": [ { @@ -2159,6 +2169,92 @@ "type": "object" } }, + { + "description": "Executes SQL queries against a GibsonAI database using the hosted Data API.\n \n This tool supports:\n - SELECT queries for data retrieval\n - INSERT queries for adding new data\n - UPDATE queries for modifying existing data \n - DELETE queries for removing data\n - Parameterized queries for security\n \n The query should be properly formatted SQL that matches your GibsonAI project's schema.\n For parameterized queries, use the 'parameters' field to pass values safely.\n \n Example queries:\n - \"SELECT * FROM users WHERE status = 'active'\"\n - \"INSERT INTO contacts (name, email) VALUES ('John Doe', 'john@example.com')\"\n - \"UPDATE products SET price = 99.99 WHERE id = 1\"\n - \"DELETE FROM orders WHERE created_at < '2023-01-01'\"", + "env_vars": [ + { + "default": null, + "description": "API key for GibsonAI Data API", + "name": "GIBSONAI_API_KEY", + "required": true + } + ], + "humanized_name": "GibsonAIQueryTool", + "init_params_schema": { + "$defs": { + "EnvVar": { + "properties": { + "default": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Default" + }, + "description": { + "title": "Description", + "type": "string" + }, + "name": { + "title": "Name", + "type": "string" + }, + "required": { + "default": true, + "title": "Required", + "type": "boolean" + } + }, + "required": [ + "name", + "description" + ], + "title": "EnvVar", + "type": "object" + } + }, + "description": "Tool for executing SQL queries against GibsonAI database via hosted Data API.\n\nThis tool allows you to execute SQL queries against your GibsonAI project database,\nsupporting data operations including SELECT, INSERT, UPDATE, and DELETE.\nIt works with any database schema deployed in your GibsonAI project.", + "properties": {}, + "title": "GibsonAIQueryTool", + "type": "object" + }, + "name": "GibsonAIQueryTool", + "package_dependencies": [], + "run_params_schema": { + "description": "Input schema for GibsonAIQueryTool.", + "properties": { + "parameters": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Optional parameters for parameterized queries. Use this for prepared statements to avoid SQL injection.", + "title": "Parameters" + }, + "query": { + "description": "SQL query to execute against the GibsonAI database. This should be a properly formatted SQL query (SELECT, INSERT, UPDATE, DELETE) that will be executed against your GibsonAI project database schema.", + "title": "Query", + "type": "string" + } + }, + "required": [ + "query" + ], + "title": "GibsonAIQueryInput", + "type": "object" + } + }, { "description": "A tool that can be used to semantic search a query from a github repo's content. This is not the GitHub API, but instead a tool that can provide semantic search capabilities.", "env_vars": [],