Tool Calling Quick Start
Get started with Captain's tool calling in 5 minutes.
What is Tool Calling?
Tool calling (function calling) lets your AI use external functions like:
- 🧮 Perform calculations
- 🌐 Call APIs
- 💾 Query databases
- 📁 Read files
- 🔧 Execute custom code
Key Point: Tools execute on your side (client), not Captain's servers. You maintain full control.
Quick Example
Python
from openai import OpenAI
client = OpenAI(
base_url="https://api.runcaptain.com/v1",
api_key="your_api_key",
default_headers={"X-Organization-ID": "your_org_id"}
)
# Define your tool
tools = [{
"type": "function",
"function": {
"name": "calculate",
"description": "Perform arithmetic",
"parameters": {
"type": "object",
"properties": {
"operation": {"type": "string", "enum": ["add", "multiply"]},
"a": {"type": "number"},
"b": {"type": "number"}
},
"required": ["operation", "a", "b"]
},
"strict": True # Required!
}
}]
# Make request
response = client.chat.completions.create(
model="captain-voyager-latest",
messages=[{"role": "user", "content": "What is 50 times 3?"}],
tools=tools
)
# Check if model wants to use tool
if response.choices[0].finish_reason == "tool_calls":
import json
tool_call = response.choices[0].message.tool_calls[0]
args = json.loads(tool_call.function.arguments)
# Execute tool on your side
if args["operation"] == "multiply":
result = args["a"] * args["b"]
print(f"Result: {result}")
TypeScript (Vercel AI SDK)
import { createOpenAI } from '@ai-sdk/openai';
import { generateText } from 'ai';
import { z } from 'zod';
const captain = createOpenAI({
apiKey: process.env.CAPTAIN_API_KEY!,
baseURL: 'https://api.runcaptain.com/v1',
headers: { 'X-Organization-ID': process.env.CAPTAIN_ORG_ID! }
});
const result = await generateText({
model: captain.chat('captain-voyager-latest'),
messages: [
{ role: 'user', content: 'What is 50 times 3?' }
],
tools: {
calculate: {
description: 'Perform arithmetic',
parameters: z.object({
operation: z.enum(['add', 'multiply']),
a: z.number(),
b: z.number()
}),
execute: async ({ operation, a, b }) => {
// Execute on your side
return operation === 'multiply' ? a * b : a + b;
}
}
},
maxSteps: 5 // Allow multiple tool calls
});
console.log(result.text);
How It Works
- You define tools with names, descriptions, and parameters
- Send request to Captain with tools
- Captain returns tool call request (if needed)
- You execute the tool in your environment
- Continue conversation with results (optional)
graph LR
A[Your App] -->|1. Request with tools| B[Captain API]
B -->|2. Tool call needed| A
A -->|3. Execute tool| C[Your Function]
C -->|4. Return result| A
Tool Definition Format
Every tool needs:
{
"type": "function", # Always "function"
"function": {
"name": "tool_name", # Unique name
"description": "...", # Clear description
"parameters": {...}, # JSON Schema
"strict": True # REQUIRED!
}
}
Common Use Cases
API Call Tool
tools = [{
"type": "function",
"function": {
"name": "get_weather",
"description": "Get current weather",
"parameters": {
"type": "object",
"properties": {
"city": {"type": "string"}
},
"required": ["city"]
},
"strict": True
}
}]
Database Query Tool
tools = [{
"type": "function",
"function": {
"name": "query_users",
"description": "Query user database",
"parameters": {
"type": "object",
"properties": {
"user_id": {"type": "string"}
},
"required": ["user_id"]
},
"strict": True
}
}]
File Operation Tool
tools = [{
"type": "function",
"function": {
"name": "read_file",
"description": "Read file contents",
"parameters": {
"type": "object",
"properties": {
"filename": {"type": "string"}
},
"required": ["filename"]
},
"strict": True
}
}]
Best Practices
✅ Do This
# Clear, specific descriptions
"description": "Calculate the sum of two numbers. Use for addition only."
# Strict parameter types
"parameters": {
"type": "object",
"properties": {
"amount": {"type": "number"},
"currency": {"type": "string", "enum": ["USD", "EUR"]}
},
"required": ["amount", "currency"]
}
# Validate inputs before execution
def execute_tool(name, args):
if name not in ALLOWED_TOOLS:
raise ValueError("Tool not allowed")
return ALLOWED_TOOLS[name](**args)
❌ Don't Do This
# Vague description
"description": "Does stuff"
# Missing types
"parameters": {
"type": "object",
"properties": {
"data": {} # What type?
}
}
# No input validation
def execute_tool(name, args):
return eval(f"{name}(**{args})") # Dangerous!
Framework Support
| Framework | Status | Multi-Turn | Best For |
|---|---|---|---|
| OpenAI Python SDK | ✅ | Manual | Python apps |
| OpenAI Node.js SDK | ✅ | Manual | Node.js apps |
| Vercel AI SDK | ✅ | Auto (maxSteps) |
Best DX |
| LangChain | ✅ | Via agents | Complex workflows |
Recommendation: Use Vercel AI SDK for automatic multi-turn handling.
Troubleshooting
Tool Not Being Called
# Make description more explicit
"description": "ALWAYS use this tool for math. Never calculate manually."
# Strengthen system prompt
messages = [
{
"role": "system",
"content": "You MUST use provided tools. Don't do calculations yourself."
},
{"role": "user", "content": "What is 5 + 3?"}
]
Empty Tool Arguments
# Check for missing args
args = json.loads(tool_call.function.arguments)
if not args.get("required_param"):
# Provide default or skip
args["required_param"] = "default"
Tool Execution Errors
# Wrap in try-catch
try:
result = execute_tool(name, args)
except Exception as e:
result = {"error": str(e)}
Next Steps
- Full Tool Calling Guide - Complete documentation
- Python Examples - More Python examples
- TypeScript Examples - More TS/JS examples
- Architecture Details - How it works internally
Get Help
- 📧 Email: support@runcaptain.com
- 📖 Docs: docs.runcaptain.com
- 🌐 Website: runcaptain.com