Connic
Connic Composer SDK

Write Tools

Learn how to create custom tools that your agents can use. Tools are plain Python functions that extend your agent's capabilities.

What are Tools?

Tools let your agents interact with the outside world

Tools are plain Python functions that agents can call during a conversation. The framework automatically wraps your functions into tools. No decorators needed. They enable agents to perform calculations, search the web or databases, send emails, access external APIs, and read/write files.

How Tool Discovery Works

Connic automatically discovers and loads your tools. Simply write Python functions in your tools/ directory with type hints on all parameters, docstrings describing the function, and return type hints.

Creating a Basic Tool

python
# tools/calculator.py

def add(a: float, b: float) -> float:
    """Add two numbers together.
    
    Args:
        a: The first number
        b: The second number
    
    Returns:
        The sum of a and b
    """
    return a + b

def multiply(a: float, b: float) -> float:
    """Multiply two numbers.
    
    Args:
        a: The first number
        b: The second number
    
    Returns:
        The product of a and b
    """
    return a * b

Docstrings are critical. The LLM uses your docstring to decide when to invoke your tool. Write clear descriptions and document all parameters in the Args section.

Required vs Optional Parameters

python
# tools/flights.py
from typing import Optional

def search_flights(
    destination: str, 
    departure_date: str, 
    flexible_days: int = 0,
    cabin_class: Optional[str] = None
) -> dict:
    """Search for available flights.
    
    Args:
        destination: The destination city (required)
        departure_date: The desired departure date (required)
        flexible_days: Number of flexible days for search. Defaults to 0.
        cabin_class: Preferred cabin class. Defaults to None.
    
    Returns:
        Dictionary with flight search results
    """
    results = {"destination": destination, "date": departure_date}
    
    if flexible_days > 0:
        results["flexible"] = True
        results["flex_days"] = flexible_days
    
    if cabin_class:
        results["cabin"] = cabin_class
    
    return results

Required: Type-hinted parameters without default values.Optional: Parameters with default values or Optional[Type] = None.

Async Tools

python
# tools/search.py
import httpx

async def web_search(query: str, num_results: int = 5) -> list[dict]:
    """Search the web for information.
    
    Args:
        query: The search query
        num_results: Number of results to return (default: 5)
    
    Returns:
        List of search results with title, url, and snippet
    """
    async with httpx.AsyncClient() as client:
        response = await client.get(
            "https://api.search.example/search",
            params={"q": query, "limit": num_results}
        )
        return response.json()["results"]

Async tools are recommended for network requests, database queries, and other I/O operations. They allow better concurrency when agents call multiple tools in parallel.

PostgreSQL Tool Example

python
# tools/postgres.py
import os
import asyncpg
from typing import Any, Dict

async def fetch_user_account(user_id: str) -> Dict[str, Any]:
    """Fetch user account details from PostgreSQL by user ID.
    
    Args:
        user_id: The user's unique ID
    
    Returns:
        A dict with account details or a not-found marker
    """
    dsn = os.environ["POSTGRES_DSN"]
    conn = await asyncpg.connect(dsn)
    try:
        row = await conn.fetchrow(
            """
            SELECT id, email, status, plan, created_at
            FROM accounts
            WHERE id = $1
            """,
            user_id,
        )
        if not row:
            return {"found": False, "user_id": user_id}
        return {"found": True, "account": dict(row)}
    finally:
        await conn.close()
yaml
# agents/account-lookup.yaml
name: account-lookup
model: gemini/gemini-2.5-flash
description: "Lookup account details during support workflows"
system_prompt: |
  Use the postgres.fetch_user_account tool to retrieve account info.
  If the account is missing, ask for the correct user ID.

tools:
  - postgres.fetch_user_account

Add asyncpg to yourrequirements.txt. Tools are auto-discovered, no decorators required. Register them by referencing postgres.fetch_user_account in the agent YAML.

Using Tools in Agents

yaml
# agents/assistant.yaml
name: assistant
model: gemini/gemini-2.5-flash
description: "Assistant with calculator and search capabilities"
instruction: |
  You are a helpful assistant with access to tools.
  Use the calculator for math and web_search for current info.

tools:
  - calculator.add
  - calculator.multiply
  - search.web_search

Use the format <filename>.<function_name> to reference your tools. The framework will automatically load and wrap them.

Accessing Run Context

Add a context parameter to your tool function to access the shared run context dictionary. Connic injects it automatically at runtime, and it is hidden from the LLM so the model never sees or fills this parameter.

python
# tools/orders.py
from typing import Any, Dict

async def get_recent_orders(limit: int = 5, context: Dict[str, Any] = {}) -> list:
    """Fetch recent orders for the current user.
    
    Args:
        limit: Number of orders to return (default: 5)
    
    Returns:
        List of recent orders
    """
    # Read values set by middleware
    user_id = context.get("user_id")
    
    # ... fetch orders from your database ...
    orders = [{"id": "ord-1", "user_id": user_id, "total": 49.99}]
    
    # Write values back to context
    context["orders_fetched"] = len(orders)
    
    return orders

The context dict is shared across middleware, prompts, and tools for the entire run. See the Context documentation for full details on how to use it.

Using Environment Variables

python
# tools/api_client.py
import os
import httpx

async def call_api(endpoint: str) -> dict:
    """Call an external API using stored credentials.
    
    Args:
        endpoint: The API endpoint to call
        
    Returns:
        API response as dictionary
    """
    # Access environment variables configured in Connic dashboard
    api_key = os.environ.get("EXTERNAL_API_KEY")
    base_url = os.environ.get("API_BASE_URL", "https://api.example.com")
    
    if not api_key:
        return {"error": "EXTERNAL_API_KEY not configured"}
    
    async with httpx.AsyncClient() as client:
        response = await client.get(
            f"{base_url}/{endpoint}",
            headers={"Authorization": f"Bearer {api_key}"}
        )
        return response.json()

Configure variables in Settings → Variables. They're injected at runtime and accessed via os.environ.get(). See the Variables documentation for more details.

Best Practices
  • Type hints are required: Parameters without type hints won't be exposed to the LLM
  • Write detailed docstrings: The LLM relies on your descriptions to know when to use each tool
  • Handle errors gracefully: Return error messages in the response instead of raising exceptions
  • Keep tools focused: One tool should do one thing well
  • Use async for I/O: Network calls, file operations, database queries
Security Considerations

Tools run with the same permissions as your Connic deployment. Always validate and sanitize inputs before using them in database queries, shell commands, or API requests.