Skip to content

Webchat Client Tools Integration

This example demonstrates how to use client-side tools with Cognipeer Webchat, allowing the AI to execute JavaScript functions directly in the user's browser during conversations.

Overview

Client tools enable powerful interactions by letting the AI:

  • Access browser APIs (geolocation, notifications, localStorage, etc.)
  • Perform calculations and data processing
  • Call external APIs from the client
  • Interact with the page DOM
  • Execute custom business logic

Quick Start

typescript
import { CognipeerWebchat, ClientTool } from '@cognipeer/sdk';

// Define client tools
const tools: ClientTool[] = [
  {
    name: 'get_current_time',
    description: 'Get the current date and time',
    execute: async () => {
      return new Date().toLocaleString();
    }
  },
  {
    name: 'calculate',
    description: 'Perform a mathematical calculation',
    parameters: {
      type: 'object',
      properties: {
        expression: {
          type: 'string',
          description: 'Mathematical expression (e.g., "2 + 2")'
        }
      },
      required: ['expression']
    },
    execute: async (args) => {
      const expression = args.expression as string;
      // Use a safe math parser in production!
      const sanitized = expression.replace(/[^0-9+\-*/().%\s]/g, '');
      const result = Function('"use strict"; return (' + sanitized + ')')();
      return `${expression} = ${result}`;
    }
  }
];

// Create webchat with tools
const webchat = new CognipeerWebchat({
  hookId: 'your-hook-id',
  tools: tools
});

// Listen to tool calls (optional)
webchat.on('tool-call', (event) => {
  console.log('Tool called:', event.data);
});

// Mount webchat
webchat.mount({
  containerId: 'chat-container',
  width: '100%',
  height: '600px'
});

Running This Example

  1. Install dependencies:
bash
npm install
  1. Set your Hook ID in .env.local:
env
NEXT_PUBLIC_COGNIPEER_BASE_URL=https://app.cognipeer.com
  1. Run the development server:
bash
npm run dev
  1. Open http://localhost:3000/webchat-widget

  2. Try asking:

    • "What time is it?"
    • "Calculate 25 * 4"

Example Tools

1. Get Current Time

typescript
{
  name: 'get_current_time',
  description: 'Get the current date and time',
  execute: async () => {
    return new Date().toLocaleString('en-US', {
      weekday: 'long',
      year: 'numeric',
      month: 'long',
      day: 'numeric',
      hour: '2-digit',
      minute: '2-digit',
      timeZoneName: 'short'
    });
  }
}

2. Get User Location

typescript
{
  name: 'get_user_location',
  description: 'Get the user\'s approximate location',
  execute: async () => {
    try {
      const response = await fetch('https://ipapi.co/json/');
      const data = await response.json();
      return JSON.stringify({
        city: data.city,
        region: data.region,
        country: data.country_name,
        timezone: data.timezone
      });
    } catch (error: any) {
      throw new Error(`Failed to get location: ${error.message}`);
    }
  }
}

3. Calculate

typescript
{
  name: 'calculate',
  description: 'Perform a mathematical calculation',
  parameters: {
    type: 'object',
    properties: {
      expression: {
        type: 'string',
        description: 'Mathematical expression to evaluate'
      }
    },
    required: ['expression']
  },
  execute: async (args) => {
    try {
      // Basic validation and sanitization
      const sanitized = args.expression.replace(/[^0-9+\-*/().%\s]/g, '');
      const result = Function('"use strict"; return (' + sanitized + ')')();
      return `${args.expression} = ${result}`;
    } catch (error: any) {
      throw new Error(`Calculation failed: ${error.message}`);
    }
  }
}

4. Browser Information

typescript
{
  name: 'get_browser_info',
  description: 'Get information about the user\'s browser and device',
  execute: async () => {
    return JSON.stringify({
      userAgent: navigator.userAgent,
      platform: navigator.platform,
      language: navigator.language,
      cookiesEnabled: navigator.cookieEnabled,
      onLine: navigator.onLine,
      screenWidth: window.screen.width,
      screenHeight: window.screen.height
    });
  }
}

5. Show Notification

typescript
{
  name: 'show_notification',
  description: 'Show a browser notification to the user',
  parameters: {
    type: 'object',
    properties: {
      title: { type: 'string', description: 'Notification title' },
      message: { type: 'string', description: 'Notification message' }
    },
    required: ['title', 'message']
  },
  execute: async (args) => {
    if (!('Notification' in window)) {
      throw new Error('Browser does not support notifications');
    }

    if (Notification.permission === 'granted') {
      new Notification(args.title, { body: args.message });
      return 'Notification displayed successfully';
    } else if (Notification.permission !== 'denied') {
      const permission = await Notification.requestPermission();
      if (permission === 'granted') {
        new Notification(args.title, { body: args.message });
        return 'Permission granted and notification displayed';
      }
    }
    
    throw new Error('Notification permission denied');
  }
}

Widget with Client Tools

typescript
import { CognipeerWebchat } from '@cognipeer/sdk';

const widget = CognipeerWebchat.createWidget({
  hookId: 'your-hook-id',
  position: 'bottom-right',
  buttonColor: '#14b8a6',
  tools: [
    {
      name: 'get_current_time',
      description: 'Get current time',
      execute: async () => new Date().toLocaleString()
    },
    {
      name: 'calculate',
      description: 'Perform calculation',
      parameters: {
        type: 'object',
        properties: {
          expression: { type: 'string' }
        },
        required: ['expression']
      },
      execute: async (args) => {
        const sanitized = args.expression.replace(/[^0-9+\-*/().%\s]/g, '');
        return String(Function('"use strict"; return (' + sanitized + ')')());
      }
    }
  ]
});

// Listen to tool execution
widget.on('tool-call', (event) => {
  console.log('Tool called:', event.data.toolName);
  console.log('Args:', event.data.args);
  console.log('Execution ID:', event.data.executionId);
});

Dynamic Tool Registration

You can also register tools after creating the webchat instance:

typescript
const webchat = new CognipeerWebchat({
  hookId: 'your-hook-id'
});

// Register a tool later
webchat.registerTool({
  name: 'my_custom_tool',
  description: 'Does something useful',
  execute: async () => {
    return 'Result';
  }
});

// Unregister when no longer needed
webchat.unregisterTool('my_custom_tool');

Best Practices

1. Clear Descriptions

Write clear, specific descriptions that help the AI understand when to use each tool:

typescript
// ✅ Good
{
  name: 'get_cart_total',
  description: 'Calculate the total price of items in the shopping cart including tax and shipping',
  execute: async () => { /* ... */ }
}

// ❌ Bad
{
  name: 'get_cart_total',
  description: 'Gets total',
  execute: async () => { /* ... */ }
}

2. Error Handling

Always handle errors gracefully and return user-friendly messages:

typescript
{
  name: 'fetch_data',
  description: 'Fetch data from API',
  execute: async () => {
    try {
      const response = await fetch('https://api.example.com/data');
      if (!response.ok) {
        throw new Error(`API returned ${response.status}`);
      }
      return await response.json();
    } catch (error: any) {
      throw new Error(`Failed to fetch data: ${error.message}`);
    }
  }
}

3. Security

  • Never use eval() on user input without sanitization
  • Validate all parameters
  • Request browser permissions properly
  • Sanitize any user-provided data
typescript
// ⚠️ Dangerous
execute: async (args) => {
  return eval(args.code); // NEVER DO THIS!
}

// ✅ Safe
execute: async (args) => {
  const sanitized = args.expression.replace(/[^0-9+\-*/().%\s]/g, '');
  return Function('"use strict"; return (' + sanitized + ')')();
}

4. Performance

  • Keep execution fast (< 5 seconds recommended)
  • Cache results when appropriate
  • Handle timeouts gracefully
  • Show loading states for long operations

How It Works

  1. Tool Registration: When you create a webchat with tools, they're registered via postMessage to the iframe
  2. AI Decision: During conversation, the AI decides when to call a tool based on the description
  3. Tool Call Event: The webchat iframe sends a tool-call event with the tool name and arguments
  4. Local Execution: Your execute function runs in the browser
  5. Result Return: The result is sent back to the iframe via tool-result event
  6. Conversation Continues: The AI uses the result to continue the conversation
User: "What time is it?"

AI decides to call get_current_time()

postMessage: { type: 'tool-call', toolName: 'get_current_time' }

Your execute() function runs

postMessage: { type: 'tool-result', output: '3:45 PM' }

AI: "It's currently 3:45 PM"

Events

Listen to tool call events for monitoring and debugging:

typescript
webchat.on('tool-call', (event) => {
  console.log({
    toolName: event.data.toolName,
    args: event.data.args,
    executionId: event.data.executionId,
    conversationId: event.data.conversationId,
    messageId: event.data.messageId
  });
});

Troubleshooting

Tool Not Being Called

  1. Check tool description - make it clear and specific
  2. Verify parameter schema is correct
  3. Check browser console for registration logs

Execution Errors

  1. Check browser console for error messages
  2. Validate args match the parameter schema
  3. Ensure execute function is async

Tool Not Registered

  1. Verify iframe is loaded properly
  2. Check Hook ID is correct
  3. Look for registration console logs

Use Cases

  • E-commerce: Calculate cart totals, check inventory, apply discount codes
  • Customer Support: Fetch account info, check order status, access user data
  • Productivity: Set reminders, create tasks, access calendar
  • Data Processing: Generate charts, transform data, parse CSV
  • Browser Integration: Read cookies, check localStorage, access device APIs

Learn More

Released under the MIT License.