Skip to content

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!
  }
});