TypeScript/JavaScript Tool Calling Examples
Clean, production-ready examples for using tool calling with Captain API in TypeScript and JavaScript.
Basic Calculator Tool (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 156 times 243?' }
],
tools: {
calculate: {
description: 'Perform arithmetic calculations',
parameters: z.object({
operation: z.enum(['add', 'subtract', 'multiply', 'divide']),
a: z.number(),
b: z.number()
}),
execute: async ({ operation, a, b }) => {
const operations = {
add: (x: number, y: number) => x + y,
subtract: (x: number, y: number) => x - y,
multiply: (x: number, y: number) => x * y,
divide: (x: number, y: number) => y !== 0 ? x / y : null
};
return { result: operations[operation](a, b) };
}
}
},
maxSteps: 5
});
console.log(result.text);
Weather API Tool
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!
}
});
async function getWeather(location: string, unit: 'celsius' | 'fahrenheit' = 'celsius') {
const response = await fetch(
`https://api.weatherapi.com/v1/current.json?key=${process.env.WEATHER_API_KEY}&q=${location}`
);
const data = await response.json();
return {
location,
temperature: unit === 'celsius' ? data.current.temp_c : data.current.temp_f,
unit,
condition: data.current.condition.text
};
}
const result = await generateText({
model: captain.chat('captain-voyager-latest'),
messages: [
{ role: 'user', content: "What's the weather in Tokyo?" }
],
tools: {
getWeather: {
description: 'Get current weather for a location',
parameters: z.object({
location: z.string().describe('City name'),
unit: z.enum(['celsius', 'fahrenheit']).optional()
}),
execute: getWeather
}
},
maxSteps: 5
});
console.log(result.text);
Database Query Tool
import { createOpenAI } from '@ai-sdk/openai';
import { generateText } from 'ai';
import { z } from 'zod';
import { Pool } from 'pg';
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 pool = new Pool({
connectionString: process.env.DATABASE_URL
});
async function queryCustomer(customerId: string) {
const result = await pool.query(
'SELECT name, email, phone FROM customers WHERE id = $1',
[customerId]
);
if (result.rows.length === 0) {
return { error: 'Customer not found' };
}
return {
name: result.rows[0].name,
email: result.rows[0].email,
phone: result.rows[0].phone
};
}
const result = await generateText({
model: captain.chat('captain-voyager-latest'),
messages: [
{ role: 'user', content: 'Get contact info for customer CUST-12345' }
],
tools: {
queryCustomer: {
description: 'Look up customer information by ID',
parameters: z.object({
customerId: z.string().describe('Customer ID')
}),
execute: async ({ customerId }) => queryCustomer(customerId)
}
},
maxSteps: 5
});
console.log(result.text);
Tool Calling with Large Context
import { createOpenAI } from '@ai-sdk/openai';
import { generateText } from 'ai';
import { z } from 'zod';
import { readFile } from 'fs/promises';
const captain = createOpenAI({
apiKey: process.env.CAPTAIN_API_KEY!,
baseURL: 'https://api.runcaptain.com/v1',
headers: {
'X-Organization-ID': process.env.CAPTAIN_ORG_ID!
}
});
// Read large financial report
const reportContent = await readFile('financial_report.txt', 'utf-8');
const result = await generateText({
model: captain.chat('captain-voyager-latest'),
messages: [
{
role: 'user',
content: 'What is the total revenue across all quarters?'
}
],
tools: {
calculateTotal: {
description: 'Calculate the sum of a list of numbers',
parameters: z.object({
numbers: z.array(z.number()).describe('List of numbers to sum')
}),
execute: async ({ numbers }) => ({
total: numbers.reduce((a, b) => a + b, 0)
})
},
calculateAverage: {
description: 'Calculate the average of a list of numbers',
parameters: z.object({
numbers: z.array(z.number()).describe('List of numbers')
}),
execute: async ({ numbers }) => ({
average: numbers.reduce((a, b) => a + b, 0) / numbers.length
})
}
},
extra_body: {
captain: {
context: reportContent
}
},
maxSteps: 5
});
console.log(result.text);
OpenAI SDK (Node.js)
import OpenAI from 'openai';
const client = new OpenAI({
apiKey: process.env.CAPTAIN_API_KEY,
baseURL: 'https://api.runcaptain.com/v1',
defaultHeaders: {
'X-Organization-ID': process.env.CAPTAIN_ORG_ID
}
});
const tools = [
{
type: 'function' as const,
function: {
name: 'calculate',
description: 'Perform arithmetic calculations',
parameters: {
type: 'object',
properties: {
operation: {
type: 'string',
enum: ['add', 'subtract', 'multiply', 'divide']
},
a: { type: 'number' },
b: { type: 'number' }
},
required: ['operation', 'a', 'b']
},
strict: true
}
}
];
const response = await client.chat.completions.create({
model: 'captain-voyager-latest',
messages: [
{ role: 'user', content: 'What is 50 + 75?' }
],
tools
});
if (response.choices[0].finish_reason === 'tool_calls') {
const toolCall = response.choices[0].message.tool_calls![0];
const args = JSON.parse(toolCall.function.arguments);
// Execute tool client-side
const operations = {
add: (a: number, b: number) => a + b,
subtract: (a: number, b: number) => a - b,
multiply: (a: number, b: number) => a * b,
divide: (a: number, b: number) => a / b
};
const result = operations[args.operation as keyof typeof operations](args.a, args.b);
console.log(`Result: ${result}`);
}
Multiple Tools
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's the current price of AAPL and its ROI if I bought at $150?"
}
],
tools: {
getStockPrice: {
description: 'Get current stock price',
parameters: z.object({
symbol: z.string().describe('Stock ticker symbol')
}),
execute: async ({ symbol }) => {
const response = await fetch(
`https://api.example.com/stocks/${symbol}`
);
return await response.json();
}
},
calculateROI: {
description: 'Calculate return on investment',
parameters: z.object({
initial: z.number().describe('Initial investment'),
current: z.number().describe('Current value')
}),
execute: async ({ initial, current }) => ({
roi: ((current - initial) / initial) * 100
})
},
getCompanyInfo: {
description: 'Get company information',
parameters: z.object({
symbol: z.string().describe('Stock ticker symbol')
}),
execute: async ({ symbol }) => {
const response = await fetch(
`https://api.example.com/companies/${symbol}`
);
return await response.json();
}
}
},
maxSteps: 10
});
console.log(result.text);
Error Handling
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!
}
});
async function safeExecute<T>(
fn: () => Promise<T>,
errorMessage: string
): Promise<T | { error: string }> {
try {
return await fn();
} catch (error) {
return {
error: `${errorMessage}: ${error instanceof Error ? error.message : 'Unknown error'}`
};
}
}
const result = await generateText({
model: captain.chat('captain-voyager-latest'),
messages: [
{ role: 'user', content: 'Your query' }
],
tools: {
riskyOperation: {
description: 'Perform an operation that might fail',
parameters: z.object({
input: z.string()
}),
execute: async ({ input }) => {
return safeExecute(
async () => {
// Your risky operation
const result = await someRiskyFunction(input);
return { result };
},
'Operation failed'
);
}
}
},
maxSteps: 5
});
console.log(result.text);
Streaming with Tools
import { createOpenAI } from '@ai-sdk/openai';
import { streamText } 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 streamText({
model: captain.chat('captain-voyager-latest'),
messages: [
{ role: 'user', content: 'What is 50 + 25?' }
],
tools: {
calculate: {
description: 'Perform calculations',
parameters: z.object({
operation: z.enum(['add', 'subtract', 'multiply', 'divide']),
a: z.number(),
b: z.number()
}),
execute: async ({ operation, a, b }) => {
const ops = {
add: (x: number, y: number) => x + y,
subtract: (x: number, y: number) => x - y,
multiply: (x: number, y: number) => x * y,
divide: (x: number, y: number) => x / y
};
return { result: ops[operation](a, b) };
}
}
},
maxSteps: 5
});
for await (const chunk of result.textStream) {
process.stdout.write(chunk);
}
Real-World Example: E-commerce Assistant
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: 'Find available products under $50 and check inventory for the cheapest one'
}
],
tools: {
searchProducts: {
description: 'Search for products with filters',
parameters: z.object({
maxPrice: z.number().optional(),
category: z.string().optional(),
inStock: z.boolean().optional()
}),
execute: async (filters) => {
const response = await fetch('https://api.yourstore.com/products', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(filters)
});
return await response.json();
}
},
checkInventory: {
description: 'Check inventory for a product',
parameters: z.object({
productId: z.string()
}),
execute: async ({ productId }) => {
const response = await fetch(
`https://api.yourstore.com/inventory/${productId}`
);
return await response.json();
}
},
getProductDetails: {
description: 'Get detailed information about a product',
parameters: z.object({
productId: z.string()
}),
execute: async ({ productId }) => {
const response = await fetch(
`https://api.yourstore.com/products/${productId}`
);
return await response.json();
}
}
},
maxSteps: 10
});
console.log(result.text);
console.log(`Tools used: ${result.toolCalls?.length || 0}`);
TypeScript Types
import { z } from 'zod';
// Define tool parameter schema
const CalculateParams = z.object({
operation: z.enum(['add', 'subtract', 'multiply', 'divide']),
a: z.number(),
b: z.number()
});
// Infer TypeScript type from schema
type CalculateParams = z.infer<typeof CalculateParams>;
// Type-safe tool execution
async function calculate(params: CalculateParams): Promise<{ result: number }> {
const operations = {
add: (x: number, y: number) => x + y,
subtract: (x: number, y: number) => x - y,
multiply: (x: number, y: number) => x * y,
divide: (x: number, y: number) => x / y
};
return {
result: operations[params.operation](params.a, params.b)
};
}
// Use in tool definition
const tools = {
calculate: {
description: 'Perform arithmetic calculations',
parameters: CalculateParams,
execute: calculate
}
};
Environment Configuration
// .env file
CAPTAIN_API_KEY=cap_dev_...
CAPTAIN_ORG_ID=019a0e27-...
// Load environment variables
import { config } from 'dotenv';
config();
// Validate required variables
const requiredEnvVars = ['CAPTAIN_API_KEY', 'CAPTAIN_ORG_ID'];
for (const envVar of requiredEnvVars) {
if (!process.env[envVar]) {
throw new Error(`Missing required environment variable: ${envVar}`);
}
}
// Create client with validated config
const captain = createOpenAI({
apiKey: process.env.CAPTAIN_API_KEY!,
baseURL: 'https://api.runcaptain.com/v1',
headers: {
'X-Organization-ID': process.env.CAPTAIN_ORG_ID!
}
});