Skip to main content
Custom tools allow you to define reusable, parameterized SQL operations that are automatically registered as MCP tools. They provide type-safe interfaces for common database queries without writing repetitive code. Custom tools are ideal for:
  • Frequently used queries: Define once, use everywhere without rewriting SQL
  • Standardized data access: Ensure consistent query patterns across your team
  • Controlled database access: Expose specific operations without granting broad SQL access
  • AI-friendly interfaces: Give AI models well-defined tools with clear parameters instead of open-ended SQL
  • Complex queries: Encapsulate JOINs, aggregations, or multi-step operations into simple tool calls
  • Parameter validation: Enforce type checking and allowed values before queries execute

Configuration

Custom tools are defined in your dbhub.toml configuration file. See the TOML Configuration documentation for complete configuration reference. Required fields:
  • name - Unique tool identifier
  • description - What the tool does (helps AI models)
  • source - Database source ID to use
  • statement - SQL query with parameter placeholders
  • parameters - Parameter definitions (optional)
See TOML Configuration - Tool Options for detailed field descriptions, parameter types, and validation rules.

Examples

Basic Query

Simple SELECT query with a single parameter:
[[tools]]
name = "get_user_by_id"
description = "Retrieve user details by their unique ID"
source = "production"
statement = "SELECT id, name, email, created_at FROM users WHERE id = $1"

[[tools.parameters]]
name = "user_id"
type = "integer"
description = "The unique user ID"

Search with Limit

Search query with optional limit parameter:
[[tools]]
name = "search_employees"
description = "Search employees by name with configurable result limit"
source = "prod_pg"
statement = "SELECT emp_no, first_name, last_name FROM employee WHERE first_name ILIKE '%' || $1 || '%' LIMIT $2"

[[tools.parameters]]
name = "search_term"
type = "string"
description = "Name to search for (case-insensitive partial match)"

[[tools.parameters]]
name = "limit"
type = "integer"
description = "Maximum number of results"
default = 10

Optional Filter with Enum

Query with optional status filter using enum validation:
[[tools]]
name = "list_orders"
description = "List orders with optional status filter"
source = "prod_pg"
statement = "SELECT * FROM orders WHERE customer_id = $1 AND ($2::text IS NULL OR status = $2)"

[[tools.parameters]]
name = "customer_id"
type = "integer"
description = "Customer ID"

[[tools.parameters]]
name = "status"
type = "string"
description = "Optional status filter"
required = false
allowed_values = ["pending", "processing", "shipped", "delivered"]

Multiple Database Types

The same tool pattern works across different databases, just adjust the parameter placeholder syntax:
[[tools]]
name = "get_product"
description = "Get product by ID"
source = "postgres_db"
statement = "SELECT * FROM products WHERE id = $1"

[[tools.parameters]]
name = "product_id"
type = "integer"
description = "Product ID"
See Parameter Placeholders for syntax reference.

Common Patterns

Flexible Filtering

Use COALESCE or NULL checks to make parameters optional:
[[tools]]
name = "search_users"
description = "Search users with optional department filter"
source = "production"
# PostgreSQL: Use NULL check for optional parameters
statement = "SELECT * FROM users WHERE ($1::text IS NULL OR department = $1) LIMIT $2"

[[tools.parameters]]
name = "department"
type = "string"
description = "Optional department filter"
required = false

[[tools.parameters]]
name = "limit"
type = "integer"
description = "Maximum results"
default = 50

Date Range Queries

[[tools]]
name = "get_orders_by_date_range"
description = "Retrieve orders within a date range"
source = "production"
statement = "SELECT * FROM orders WHERE created_at BETWEEN $1 AND $2 ORDER BY created_at DESC"

[[tools.parameters]]
name = "start_date"
type = "string"
description = "Start date (ISO 8601 format: YYYY-MM-DD)"

[[tools.parameters]]
name = "end_date"
type = "string"
description = "End date (ISO 8601 format: YYYY-MM-DD)"

Aggregation Queries

[[tools]]
name = "get_sales_summary"
description = "Get sales summary by product category"
source = "production"
statement = """
  SELECT
    category,
    COUNT(*) as total_orders,
    SUM(amount) as total_revenue,
    AVG(amount) as avg_order_value
  FROM orders
  WHERE status = $1
  GROUP BY category
  ORDER BY total_revenue DESC
"""

[[tools.parameters]]
name = "status"
type = "string"
description = "Order status"
allowed_values = ["completed", "pending", "cancelled"]

Security & Validation

SQL Injection Protection

Custom tools use parameterized queries, which provide automatic protection against SQL injection attacks. Parameter values are never interpolated directly into SQL strings.
Always use parameter placeholders. Never concatenate user input into SQL statements.

Readonly Mode

The readonly setting on execute_sql only affects that tool. Custom tools are controlled by their SQL statement - DBHub analyzes the statement to determine if it’s read-only.
# Configure execute_sql as read-only
[[tools]]
name = "execute_sql"
source = "prod_pg"
readonly = true

# Custom tools are unaffected by readonly setting
[[tools]]
name = "get_user_by_id"
source = "prod_pg"
description = "Get user details"
statement = "SELECT * FROM users WHERE id = $1"
[[tools.parameters]]
name = "user_id"
type = "integer"
description = "User ID"

Max Rows Enforcement

For custom tools with parameterized LIMIT clauses, the connector’s max_rows setting still applies:
[[tools]]
name = "execute_sql"
source = "prod_pg"
max_rows = 1000  # Hard limit on SELECT results

[[tools]]
name = "search_users"
source = "prod_pg"
description = "Search users with limit"
statement = "SELECT * FROM users WHERE active = $1 LIMIT $2"
# Even if $2 is 5000, max_rows=1000 will cap the results

Startup Validation

Tools are validated when the server starts:
  • All required fields must be present
  • The specified source must exist
  • Tool names must be unique and cannot conflict with built-in tools (execute_sql, search_objects)
  • Parameter count must match SQL placeholders
  • Parameter types must be valid
If validation fails, the server will not start and will display detailed error messages.

Tool Response Format

Custom tools return the same response format as execute_sql: Success:
{
  "success": true,
  "rows": [
    {
      "id": 12345,
      "name": "Alice Johnson",
      "email": "[email protected]",
      "created_at": "2024-01-15T10:30:00Z"
    }
  ],
  "count": 1,
  "source_id": "prod_pg"
}
Error:
{
  "success": false,
  "error": "Parameter validation failed: user_id: Required",
  "code": "EXECUTION_ERROR"
}

Best Practices

  • Use descriptive names: Tool names should clearly indicate their purpose (e.g., get_active_users_by_department rather than query1)
  • Write detailed descriptions: Help AI models understand when to use the tool by providing clear, complete descriptions
  • Document parameter constraints: Include units, ranges, and format expectations in parameter descriptions
  • Leverage enums: Use allowed_values for parameters with a fixed set of valid options
  • Provide defaults: Make tools easier to use by providing sensible defaults for optional parameters
  • Keep tools focused: Each tool should perform a single, well-defined operation
  • Test parameter combinations: Ensure optional parameters work correctly in all combinations
  • Use multi-line strings: For complex SQL, use TOML’s """ multi-line syntax for readability

See Also