npm package: @mcp-fe/event-tracker
Description: Framework-agnostic event tracking library for the MCP-FE (Model Context Protocol - Frontend Edge) ecosystem. This library provides a simple, clean API for tracking user interactions and making them available to AI agents through the MCP protocol.
Latest version: 0.2.2

# @mcp-fe/event-tracker

Framework-agnostic event tracking library for the MCP-FE (Model Context Protocol - Frontend Edge) ecosystem. This library provides a simple, clean API for tracking user interactions and making them available to AI agents through the MCP protocol.

## Overview

`@mcp-fe/event-tracker` is a lightweight wrapper around [`@mcp-fe/mcp-worker`](../mcp-worker/README.md) that provides an ergonomic API for tracking user events in any JavaScript application. It serves as the foundation for framework-specific integrations like [`@mcp-fe/react-event-tracker`](../react-event-tracker/README.md).

### Key Features

- **Framework Agnostic**: Works with any JavaScript framework or vanilla JS
- **Simple API**: Clean, intuitive functions for common tracking needs
- **Rich Event Data**: Captures element details, metadata, and context
- **Connection Management**: Built-in WebSocket connection status monitoring
- **Authentication Support**: Token-based authentication for user-specific events
- **TypeScript Support**: Full type definitions included

## Installation

```bash
npm install @mcp-fe/event-tracker
# or
pnpm add @mcp-fe/event-tracker
# or
yarn add @mcp-fe/event-tracker
```

### Dependencies

This package requires [`@mcp-fe/mcp-worker`](../mcp-worker/README.md) which is automatically installed as a dependency. Make sure to set up the worker files as described in the mcp-worker documentation.

## Quick Start

```typescript
import { 
  initEventTracker, 
  trackEvent, 
  trackNavigation, 
  trackClick 
} from '@mcp-fe/event-tracker';

// Initialize the event tracker
await initEventTracker({
  backendWsUrl: 'ws://localhost:3001'
});

// Track user interactions
await trackNavigation('/home', '/products');
await trackClick(document.getElementById('buy-button'));

// Track custom events
await trackEvent({
  type: 'custom',
  metadata: {
    eventName: 'purchase_completed',
    orderId: '12345',
    amount: 99.99
  }
});
```

## API Reference

### Initialization

#### `initEventTracker(options?)`

Initialize the event tracking system. This sets up the underlying MCP worker.

**Parameters:**
- `options?: ServiceWorkerRegistration | WorkerClientInitOptions`

**WorkerClientInitOptions:**
```typescript
interface WorkerClientInitOptions {
  sharedWorkerUrl?: string;    // Default: '/mcp-shared-worker.js'
  serviceWorkerUrl?: string;   // Default: '/mcp-service-worker.js'
  backendWsUrl?: string;       // Default: 'ws://localhost:3001'
}
```

**Examples:**
```typescript
// Basic initialization
await initEventTracker();

// With custom WebSocket URL
await initEventTracker({
  backendWsUrl: 'wss://my-mcp-server.com/ws'
});

// With custom worker paths
await initEventTracker({
  sharedWorkerUrl: '/assets/workers/mcp-shared-worker.js',
  backendWsUrl: 'ws://localhost:3001'
});

// Using existing ServiceWorker registration
const registration = await navigator.serviceWorker.register('/mcp-service-worker.js');
await initEventTracker(registration);
```

### Event Tracking

#### `trackEvent(eventData)`

Track a generic user event with custom data.

**Parameters:**
- `eventData: UserEventData`

**UserEventData Interface:**
```typescript
interface UserEventData {
  type: 'navigation' | 'click' | 'input' | 'custom';
  path?: string;           // Current page path
  from?: string;           // Previous location (navigation events)
  to?: string;             // Next location (navigation events)
  element?: string;        // HTML element tag name
  elementId?: string;      // Element ID attribute
  elementClass?: string;   // Element CSS classes
  elementText?: string;    // Element text content (truncated to 100 chars)
  metadata?: Record<string, unknown>; // Additional event data
}
```

**Example:**
```typescript
await trackEvent({
  type: 'custom',
  path: '/dashboard',
  metadata: {
    eventName: 'feature_used',
    feature: 'export_data',
    format: 'csv',
    recordCount: 150
  }
});
```

#### `trackNavigation(from, to, path?)`

Track navigation between routes or pages.

**Parameters:**
- `from: string` - Previous route/URL
- `to: string` - Current route/URL  
- `path?: string` - Optional explicit path (defaults to `to`)

**Example:**
```typescript
// Basic navigation tracking
await trackNavigation('/home', '/products');

// With explicit path
await trackNavigation('/users/123', '/users/456', '/users');
```

#### `trackClick(element, path?, metadata?)`

Track click events on HTML elements with automatic element data extraction.

**Parameters:**
- `element: HTMLElement` - The clicked element
- `path?: string` - Current page path (defaults to `window.location.pathname`)
- `metadata?: Record<string, unknown>` - Additional click data

**Example:**
```typescript
// Track button click
const button = document.getElementById('submit-btn');
await trackClick(button);

// With additional metadata
const link = document.querySelector('a[data-product-id="123"]');
await trackClick(link, '/products', {
  productId: '123',
  category: 'electronics',
  price: 299.99
});

// Event listener example
document.addEventListener('click', async (event) => {
  if (event.target instanceof HTMLElement) {
    await trackClick(event.target);
  }
});
```

#### `trackInput(element, value?, path?)`

Track input changes in forms with debouncing and value length tracking.

**Parameters:**
- `element: HTMLElement` - The input element
- `value?: string` - Input value (stored securely with length tracking)
- `path?: string` - Current page path

**Example:**
```typescript
// Track input change
const emailInput = document.getElementById('email');
await trackInput(emailInput, emailInput.value);

// Event listener with debouncing
let timeoutId;
document.addEventListener('input', (event) => {
  if (event.target instanceof HTMLInputElement) {
    clearTimeout(timeoutId);
    timeoutId = setTimeout(async () => {
      await trackInput(event.target, event.target.value);
    }, 1000); // 1 second debounce
  }
});
```

#### `trackCustom(eventName, metadata?, path?)`

Convenient wrapper for tracking custom business events.

**Parameters:**
- `eventName: string` - Name of the custom event
- `metadata?: Record<string, unknown>` - Event-specific data
- `path?: string` - Current page path

**Example:**
```typescript
// Track business events
await trackCustom('purchase_completed', {
  orderId: '12345',
  amount: 99.99,
  currency: 'USD',
  items: ['product-1', 'product-2']
});

await trackCustom('search_performed', {
  query: 'javascript tutorials',
  results: 42,
  filters: ['beginner', 'video']
});

await trackCustom('error_occurred', {
  errorType: 'validation',
  field: 'email',
  message: 'Invalid email format'
});
```

### Connection Management

#### `getConnectionStatus()`

Check if the MCP worker is connected to the proxy server.

**Returns:** `Promise<boolean>`

**Example:**
```typescript
const isConnected = await getConnectionStatus();
if (!isConnected) {
  console.warn('MCP connection lost, events may not be available to AI agents');
}
```

#### `onConnectionStatus(callback)` / `offConnectionStatus(callback)`

Subscribe to connection status changes.

**Parameters:**
- `callback: (connected: boolean) => void` - Status change callback

**Example:**
```typescript
const handleConnectionChange = (connected) => {
  console.log('MCP connection:', connected ? 'Connected' : 'Disconnected');
  
  // Update UI indicator
  const indicator = document.getElementById('connection-status');
  indicator.className = connected ? 'connected' : 'disconnected';
  indicator.textContent = connected ? '🟢 Connected' : '🔴 Offline';
};

// Start listening
onConnectionStatus(handleConnectionChange);

// Stop listening (e.g., on component unmount)
offConnectionStatus(handleConnectionChange);
```

### Authentication

#### `setAuthToken(token)`

Set authentication token for associating events with specific users.

**Parameters:**
- `token: string` - Authentication token (e.g., JWT)

**Example:**
```typescript
// After user login
async function handleLogin(credentials) {
  const response = await authenticateUser(credentials);
  
  // Set token for MCP tracking
  setAuthToken(response.accessToken);
  
  // Track login event
  await trackCustom('user_logged_in', {
    userId: response.user.id,
    method: 'password'
  });
}

// On logout
async function handleLogout() {
  await trackCustom('user_logged_out');
  
  // Clear authentication
  setAuthToken('');
}
```


## Integration with AI Agents

Once events are tracked and stored, AI agents can query them through the MCP protocol:

```json
{
  "jsonrpc": "2.0",
  "method": "tools/call", 
  "params": {
    "name": "get_user_events",
    "arguments": {
      "type": "click",
      "limit": 10,
      "startTime": 1705712400000
    }
  }
}
```

This enables AI agents to:
- Provide context-aware customer support
- Debug user interface issues
- Analyze user behavior patterns
- Guide users through complex workflows
- Understand error scenarios and user frustration points

## Best Practices

### Event Granularity

```typescript
// ✅ Good: Track meaningful interactions
await trackClick(submitButton, '/checkout', {
  step: 'payment',
  amount: totalAmount
});

// ❌ Too granular: Don't track every mouseover
document.addEventListener('mouseover', trackEvent); // Avoid this
```

### Sensitive Data

```typescript
// ✅ Good: Track input activity without sensitive values
await trackInput(passwordInput, undefined, '/login'); // Don't pass actual password

// ✅ Good: Track metadata about sensitive operations
await trackCustom('payment_submitted', {
  amount: 99.99,
  currency: 'USD',
  // Don't include card numbers or CVV
});
```

### Performance

```typescript
// ✅ Good: Debounce high-frequency events
let searchTimeout;
searchInput.addEventListener('input', () => {
  clearTimeout(searchTimeout);
  searchTimeout = setTimeout(async () => {
    await trackInput(searchInput, searchInput.value);
  }, 500);
});

// ✅ Good: Batch related events
await Promise.all([
  trackCustom('form_submitted'),
  trackNavigation('/form', '/success')
]);
```

## Error Handling

```typescript
// Wrap tracking calls in try-catch for production resilience
async function safeTrackEvent(eventData) {
  try {
    await trackEvent(eventData);
  } catch (error) {
    // Log error but don't break user experience
    console.warn('Failed to track event:', error);
  }
}

// Check connection before critical tracking
const isConnected = await getConnectionStatus();
if (isConnected) {
  await trackCustom('critical_business_event', eventData);
} else {
  // Store locally or queue for later
  localStorage.setItem('pending_events', JSON.stringify([eventData]));
}
```

## Troubleshooting

### Events Not Being Stored

1. **Check worker initialization**: Ensure `initEventTracker()` completed successfully
2. **Verify worker files**: Make sure MCP worker files are in your public directory
3. **Check connection**: Use `getConnectionStatus()` to verify proxy connection

### Connection Issues

1. **Proxy server**: Verify the MCP proxy server is running at the specified WebSocket URL
2. **CORS**: Check browser console for CORS errors
3. **WebSocket**: Verify WebSocket connection in browser Developer Tools

### Memory Usage

1. **Event cleanup**: Periodically clean old events from IndexedDB
2. **Debouncing**: Use appropriate debouncing for high-frequency events
3. **Selective tracking**: Only track events that provide value to AI agents

## Requirements

- **Modern Browser**: ES2020+ support
- **MCP Worker Setup**: Requires [`@mcp-fe/mcp-worker`](../mcp-worker/README.md) configuration
- **MCP Proxy Server**: A running MCP proxy server to collect events

## Related Packages

- **[@mcp-fe/mcp-worker](../mcp-worker/README.md)**: Core MCP worker implementation
- **[@mcp-fe/react-event-tracker](../react-event-tracker/README.md)**: React-specific integration
- **[Main MCP-FE Project](../../README.md)**: Complete ecosystem documentation

## Credits

Created and maintained with ❤️ by [Michal Kopecký](https://github.com/kopecmi8).

## License

Licensed under the Apache License, Version 2.0. See the [LICENSE](../../LICENSE) file for details.