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