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.
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
# 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 * bDocstrings 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
# 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 resultsRequired: Type-hinted parameters without default values.Optional: Parameters with default values or Optional[Type] = None.
Async Tools
# 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
# 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()# 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_accountAdd 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
# 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_searchUse 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.
# 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 ordersThe 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
# 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.
- 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
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.