-
-
Notifications
You must be signed in to change notification settings - Fork 0
Adding Tools
Kohlbern Jary edited this page Dec 9, 2025
·
1 revision
This guide explains how to add new tools that Cass can use during conversations.
Tools allow Cass to perform actions: create calendar events, manage tasks, search journals, etc. Each tool has:
- Definition - Schema describing the tool's name, description, and parameters
- Handler - Function that executes the tool
- Routing - Logic to include the tool and route calls to the handler
Create a new file in backend/handlers/:
# backend/handlers/myfeature.py
"""
MyFeature tool handler - enables Cass to do X, Y, Z.
"""
from typing import Dict, Any
# Tool definitions (Anthropic format)
MYFEATURE_TOOLS = [
{
"name": "myfeature_action",
"description": "Does something useful. Call this when the user asks about X.",
"input_schema": {
"type": "object",
"properties": {
"required_param": {
"type": "string",
"description": "What this parameter is for"
},
"optional_param": {
"type": "integer",
"description": "Optional setting",
"default": 10
}
},
"required": ["required_param"]
}
},
{
"name": "myfeature_query",
"description": "Retrieves information about X.",
"input_schema": {
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "What to search for"
}
},
"required": ["query"]
}
}
]
async def execute_myfeature_tool(
tool_name: str,
tool_input: Dict,
# Add any dependencies your tools need:
# storage: MyFeatureStorage,
# user_id: str,
) -> Dict[str, Any]:
"""
Execute a myfeature tool.
Returns dict with 'success' bool and either 'result' or 'error'.
"""
try:
if tool_name == "myfeature_action":
return await _do_action(tool_input)
elif tool_name == "myfeature_query":
return await _do_query(tool_input)
else:
return {"success": False, "error": f"Unknown tool: {tool_name}"}
except Exception as e:
return {"success": False, "error": str(e)}
async def _do_action(tool_input: Dict) -> Dict[str, Any]:
"""Handle myfeature_action tool."""
required_param = tool_input.get("required_param")
optional_param = tool_input.get("optional_param", 10)
# Do the actual work here
result = f"Did action with {required_param}"
return {
"success": True,
"result": result
}
async def _do_query(tool_input: Dict) -> Dict[str, Any]:
"""Handle myfeature_query tool."""
query = tool_input.get("query", "")
# Do the query
results = [] # Your query logic here
return {
"success": True,
"result": f"Found {len(results)} results",
"data": results
}# backend/handlers/__init__.py
from .calendar import execute_calendar_tool, CALENDAR_TOOLS
from .tasks import execute_task_tool, TASK_TOOLS
from .journals import execute_journal_tool, JOURNAL_TOOLS
from .myfeature import execute_myfeature_tool, MYFEATURE_TOOLS # Add thisIn backend/agent_client.py, add keywords that trigger your tools:
# Near the top with other keyword sets
MYFEATURE_KEYWORDS = frozenset({
"myfeature", "related", "terms", "that", "users", "might", "say"
})
# In the function that checks keywords
def _should_include_myfeature_tools(message: str) -> bool:
"""Check if message might need myfeature tools."""
message_lower = message.lower()
return any(kw in message_lower for kw in MYFEATURE_KEYWORDS)In agent_client.py, include tools when keywords match:
def _get_tools_for_message(message: str) -> List[Dict]:
"""Get relevant tools based on message content."""
tools = BASE_TOOLS.copy()
if _should_include_calendar_tools(message):
tools.extend(CALENDAR_TOOLS)
if _should_include_myfeature_tools(message): # Add this
tools.extend(MYFEATURE_TOOLS)
return toolsIn the WebSocket handler, add routing for your tools:
# In the tool processing section of handle_websocket()
elif tool_name.startswith("myfeature_"):
from handlers import execute_myfeature_tool
result = await execute_myfeature_tool(
tool_name,
tool_input,
# Pass any dependencies
)- Use
feature_actionformat:calendar_create_event,task_add,journal_search - Prefix groups related tools:
wiki_create_page,wiki_search,wiki_update
Write descriptions that help the LLM understand when to use the tool:
# Good - explains when to use
"description": "Create a new calendar event. Use when the user wants to schedule something."
# Bad - just says what it does
"description": "Creates an event."Be specific about expected values:
# Good
"date": {
"type": "string",
"description": "Date in YYYY-MM-DD format, e.g., '2025-01-15'"
}
# Bad
"date": {
"type": "string",
"description": "The date"
}- Mark parameters as
requiredonly if the tool can't function without them - Provide sensible defaults for optional parameters
- Document defaults in the description
Tools should return a dict that the LLM can understand:
# Success case
{
"success": True,
"result": "Human-readable result message",
"data": {...} # Optional structured data
}
# Error case
{
"success": False,
"error": "Human-readable error message"
}- Start the backend
- Send a message that triggers your keywords
- Check logs for tool calls
- Verify tool result is returned to LLM
# tests/test_myfeature_tools.py
import pytest
from handlers.myfeature import execute_myfeature_tool
@pytest.mark.asyncio
async def test_myfeature_action():
result = await execute_myfeature_tool(
"myfeature_action",
{"required_param": "test"}
)
assert result["success"] is True
assert "test" in result["result"]# handlers/journals.py
JOURNAL_TOOLS = [
{
"name": "search_journals",
"description": "Search through past journal entries...",
"input_schema": {
"type": "object",
"properties": {
"query": {"type": "string", "description": "Search query"},
"limit": {"type": "integer", "description": "Max results", "default": 5}
},
"required": ["query"]
}
}
]# handlers/calendar.py
{
"name": "create_event",
"description": "Create a new calendar event...",
"input_schema": {
"type": "object",
"properties": {
"title": {"type": "string"},
"date": {"type": "string", "description": "YYYY-MM-DD"},
"time": {"type": "string", "description": "HH:MM (24-hour)"},
"duration_minutes": {"type": "integer", "default": 60},
"description": {"type": "string"},
"location": {"type": "string"}
},
"required": ["title", "date"]
}
}- Check keywords are being matched (add logging)
- Verify tool is in the tools list sent to LLM
- Check tool description is clear enough for LLM
- Check the return format includes
successanderror/result - Verify error is being caught and returned, not raised
- Check
requiredarray in schema - Verify parameter descriptions are clear
- Check for typos in parameter names