> ## Documentation Index
> Fetch the complete documentation index at: https://haiprotocol.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Tools

> Model Context Protocol (MCP) integration, tool registration, and external service connectivity in the HAIP SDK

<Note>
  **New protocol version released**: This page may contain outdated information.
</Note>

Extend your HAIP applications with powerful tool integration capabilities. Leverage
the Model Context Protocol (MCP) to connect AI agents with external services, APIs,
and custom tools for enhanced functionality.

## Tool Integration Overview

### What are Tools?

Tools are external functions or services that AI agents can call to:

* Retrieve information (web search, database queries)
* Perform actions (send emails, create files)
* Process data (calculations, image analysis)
* Interact with systems (APIs, databases)

### MCP Integration

The HAIP SDK implements the Model Context Protocol (MCP) for tool lifecycle management:

```typescript theme={null}
// Register available tools
await client.listTools("AGENT", [
  { name: "search", description: "Search the web for information" },
  { name: "calculator", description: "Perform mathematical calculations" },
  { name: "weather", description: "Get weather information for a location" },
]);

// Handle tool calls
client.on("message", (message: HAIPMessage) => {
  if (message.type === "TOOL_CALL") {
    handleToolCall(message);
  }
});
```

## Tool Lifecycle

### 1. Tool Registration

Register available tools with the agent:

```typescript theme={null}
// Define tools
const tools = [
  {
    name: "search",
    description: "Search the web for information",
    parameters: {
      type: "object",
      properties: {
        query: {
          type: "string",
          description: "Search query",
        },
      },
      required: ["query"],
    },
  },
  {
    name: "calculator",
    description: "Perform mathematical calculations",
    parameters: {
      type: "object",
      properties: {
        expression: {
          type: "string",
          description: "Mathematical expression to evaluate",
        },
      },
      required: ["expression"],
    },
  },
];

// Register tools
await client.listTools("AGENT", tools);
```

### 2. Tool Call Handling

Handle incoming tool calls:

```typescript theme={null}
async function handleToolCall(message: HAIPMessage) {
  const { tool, params, call_id } = message.payload;

  try {
    // Update tool status to running
    await client.updateTool("AGENT", call_id, "RUNNING", 0);

    // Execute the tool
    const result = await executeTool(tool, params);

    // Complete the tool call
    await client.completeTool("AGENT", call_id, "OK", result);
  } catch (error) {
    console.error(`Tool execution failed:`, error);
    await client.completeTool("AGENT", call_id, "ERROR", {
      error: error.message,
    });
  }
}

async function executeTool(tool: string, params: any) {
  switch (tool) {
    case "search":
      return await performSearch(params.query);

    case "calculator":
      return { result: eval(params.expression) };

    case "weather":
      return await getWeather(params.location);

    default:
      throw new Error(`Unknown tool: ${tool}`);
  }
}
```

### 3. Tool Status Updates

Update tool execution status:

```typescript theme={null}
// Tool is queued
await client.updateTool("AGENT", callId, "QUEUED");

// Tool is running with progress
await client.updateTool("AGENT", callId, "RUNNING", 50); // 50% progress

// Tool is running with partial results
await client.updateTool("AGENT", callId, "RUNNING", 75, {
  partial: "Partial results available...",
});

// Tool is being cancelled
await client.updateTool("AGENT", callId, "CANCELLING");
```

### 4. Tool Completion

Complete tool execution with results:

```typescript theme={null}
// Successful completion
await client.completeTool("AGENT", callId, "OK", {
  results: ["Result 1", "Result 2"],
  metadata: { source: "web-search" },
});

// Error completion
await client.completeTool("AGENT", callId, "ERROR", {
  error: "Network timeout",
  details: "Failed to connect to search service",
});

// Cancelled completion
await client.completeTool("AGENT", callId, "CANCELLED", {
  reason: "User cancelled the operation",
});
```

## Tool Implementation Examples

### Web Search Tool

```typescript theme={null}
class WebSearchTool {
  constructor(private apiKey: string) {}

  async search(query: string): Promise<any> {
    const response = await fetch(
      `https://api.search.com/search?q=${encodeURIComponent(query)}&key=${
        this.apiKey
      }`
    );

    if (!response.ok) {
      throw new Error(`Search failed: ${response.statusText}`);
    }

    const data = await response.json();
    return {
      results: data.results.map((result: any) => ({
        title: result.title,
        url: result.url,
        snippet: result.snippet,
      })),
      totalResults: data.totalResults,
    };
  }
}

// Usage in tool handler
const searchTool = new WebSearchTool(process.env.SEARCH_API_KEY);

async function handleSearchTool(params: any) {
  const results = await searchTool.search(params.query);
  return {
    query: params.query,
    results: results.results,
    totalResults: results.totalResults,
  };
}
```

### Calculator Tool

```typescript theme={null}
class CalculatorTool {
  private safeEval(expression: string): number {
    // Remove potentially dangerous characters
    const sanitized = expression.replace(/[^0-9+\-*/().\s]/g, "");

    try {
      // Use Function constructor for safer evaluation
      const result = new Function(`return ${sanitized}`)();

      if (typeof result !== "number" || !isFinite(result)) {
        throw new Error("Invalid calculation result");
      }

      return result;
    } catch (error) {
      throw new Error(`Calculation failed: ${error.message}`);
    }
  }

  calculate(expression: string): any {
    const result = this.safeEval(expression);
    return {
      expression,
      result,
      formatted: result.toLocaleString(),
    };
  }
}

// Usage
const calculator = new CalculatorTool();

async function handleCalculatorTool(params: any) {
  return calculator.calculate(params.expression);
}
```

### Weather Tool

```typescript theme={null}
class WeatherTool {
  constructor(private apiKey: string) {}

  async getWeather(location: string): Promise<any> {
    const response = await fetch(
      `https://api.weatherapi.com/v1/current.json?key=${
        this.apiKey
      }&q=${encodeURIComponent(location)}`
    );

    if (!response.ok) {
      throw new Error(`Weather API failed: ${response.statusText}`);
    }

    const data = await response.json();
    return {
      location: data.location.name,
      temperature: data.current.temp_c,
      condition: data.current.condition.text,
      humidity: data.current.humidity,
      windSpeed: data.current.wind_kph,
    };
  }
}

// Usage
const weatherTool = new WeatherTool(process.env.WEATHER_API_KEY);

async function handleWeatherTool(params: any) {
  return await weatherTool.getWeather(params.location);
}
```

## Tool Management

### Tool Registry

Manage multiple tools in a registry:

```typescript theme={null}
class ToolRegistry {
  private tools = new Map<string, (params: any) => Promise<any>>();

  register(name: string, handler: (params: any) => Promise<any>) {
    this.tools.set(name, handler);
  }

  async execute(name: string, params: any): Promise<any> {
    const handler = this.tools.get(name);
    if (!handler) {
      throw new Error(`Tool not found: ${name}`);
    }

    return await handler(params);
  }

  getToolNames(): string[] {
    return Array.from(this.tools.keys());
  }
}

// Usage
const registry = new ToolRegistry();

registry.register("search", handleSearchTool);
registry.register("calculator", handleCalculatorTool);
registry.register("weather", handleWeatherTool);

// Execute tool
const result = await registry.execute("search", { query: "TypeScript" });
```

### Tool Schema Management

Get tool schemas for validation:

```typescript theme={null}
async function getToolSchema(toolName: string) {
  await client.getToolSchema("AGENT", toolName);
}

// Handle schema response
client.on("message", (message: HAIPMessage) => {
  if (message.type === "TOOL_SCHEMA") {
    const { tool, schema } = message.payload;
    console.log(`Schema for ${tool}:`, schema);
  }
});
```

## Tool Error Handling

### Comprehensive Error Handling

```typescript theme={null}
class ToolErrorHandler {
  static async handleToolExecution(
    client: any,
    callId: string,
    toolName: string,
    params: any,
    executor: (params: any) => Promise<any>
  ) {
    try {
      // Update status to running
      await client.updateTool("AGENT", callId, "RUNNING", 0);

      // Execute with timeout
      const result = await Promise.race([
        executor(params),
        new Promise((_, reject) =>
          setTimeout(() => reject(new Error("Tool execution timeout")), 30000)
        ),
      ]);

      // Complete successfully
      await client.completeTool("AGENT", callId, "OK", result);
    } catch (error) {
      console.error(`Tool ${toolName} failed:`, error);

      // Determine error type
      let errorType = "ERROR";
      let errorDetails = { error: error.message };

      if (error.message.includes("timeout")) {
        errorType = "ERROR";
        errorDetails = { error: "Tool execution timed out" };
      } else if (error.message.includes("permission")) {
        errorType = "ERROR";
        errorDetails = { error: "Insufficient permissions for this tool" };
      } else if (error.message.includes("network")) {
        errorType = "ERROR";
        errorDetails = { error: "Network error occurred" };
      }

      await client.completeTool("AGENT", callId, errorType, errorDetails);
    }
  }
}

// Usage
await ToolErrorHandler.handleToolExecution(
  client,
  callId,
  "search",
  { query: "TypeScript" },
  handleSearchTool
);
```

### Tool Retry Logic

```typescript theme={null}
class ToolRetryHandler {
  static async executeWithRetry(
    executor: () => Promise<any>,
    maxRetries: number = 3,
    delay: number = 1000
  ): Promise<any> {
    let lastError: Error;

    for (let attempt = 1; attempt <= maxRetries; attempt++) {
      try {
        return await executor();
      } catch (error) {
        lastError = error as Error;

        if (attempt < maxRetries) {
          console.log(
            `Tool attempt ${attempt} failed, retrying in ${delay}ms...`
          );
          await new Promise((resolve) => setTimeout(resolve, delay));
          delay *= 2; // Exponential backoff
        }
      }
    }

    throw lastError!;
  }
}

// Usage in tool handler
async function handleSearchToolWithRetry(params: any) {
  return await ToolRetryHandler.executeWithRetry(
    () => searchTool.search(params.query),
    3,
    1000
  );
}
```

## Tool Testing

### Mock Tool Testing

```typescript theme={null}
class MockToolRegistry {
  private mockResults = new Map<string, any>();
  private mockErrors = new Map<string, Error>();

  setMockResult(toolName: string, result: any) {
    this.mockResults.set(toolName, result);
  }

  setMockError(toolName: string, error: Error) {
    this.mockErrors.set(toolName, error);
  }

  async execute(toolName: string, params: any): Promise<any> {
    const error = this.mockErrors.get(toolName);
    if (error) {
      throw error;
    }

    const result = this.mockResults.get(toolName);
    if (result) {
      return result;
    }

    throw new Error(`No mock result set for tool: ${toolName}`);
  }
}

// Usage in tests
const mockRegistry = new MockToolRegistry();

mockRegistry.setMockResult("search", {
  results: [{ title: "Test Result", url: "https://example.com" }],
  totalResults: 1,
});

mockRegistry.setMockError("calculator", new Error("Invalid expression"));

// Test tool execution
const result = await mockRegistry.execute("search", { query: "test" });
expect(result.results).toHaveLength(1);
```

### Tool Integration Testing

```typescript theme={null}
describe("Tool Integration", () => {
  test("should handle tool call successfully", async () => {
    const client = createHAIPClient({
      url: "ws://localhost:8080",
      token: "test-token",
      transport: "websocket",
    });

    // Mock tool execution
    const mockToolCall = {
      type: "TOOL_CALL",
      payload: {
        tool: "calculator",
        params: { expression: "2 + 2" },
        call_id: "call-123",
      },
    };

    // Set up expectations
    const updateSpy = jest.spyOn(client, "updateTool");
    const completeSpy = jest.spyOn(client, "completeTool");

    // Simulate tool call
    client.emit("message", mockToolCall);

    // Wait for processing
    await new Promise((resolve) => setTimeout(resolve, 100));

    // Verify tool lifecycle
    expect(updateSpy).toHaveBeenCalledWith("AGENT", "call-123", "RUNNING", 0);
    expect(completeSpy).toHaveBeenCalledWith("AGENT", "call-123", "OK", {
      result: 4,
    });
  });
});
```

## Tool Performance Monitoring

### Tool Metrics

```typescript theme={null}
class ToolMetrics {
  private metrics = new Map<
    string,
    {
      calls: number;
      totalTime: number;
      errors: number;
      lastCall: number;
    }
  >();

  recordCall(toolName: string, duration: number, success: boolean) {
    const current = this.metrics.get(toolName) || {
      calls: 0,
      totalTime: 0,
      errors: 0,
      lastCall: 0,
    };

    current.calls++;
    current.totalTime += duration;
    current.lastCall = Date.now();

    if (!success) {
      current.errors++;
    }

    this.metrics.set(toolName, current);
  }

  getMetrics(toolName?: string) {
    if (toolName) {
      return this.metrics.get(toolName);
    }
    return Object.fromEntries(this.metrics);
  }

  getAverageTime(toolName: string): number {
    const metrics = this.metrics.get(toolName);
    if (!metrics || metrics.calls === 0) {
      return 0;
    }
    return metrics.totalTime / metrics.calls;
  }

  getErrorRate(toolName: string): number {
    const metrics = this.metrics.get(toolName);
    if (!metrics || metrics.calls === 0) {
      return 0;
    }
    return metrics.errors / metrics.calls;
  }
}

// Usage
const toolMetrics = new ToolMetrics();

async function executeToolWithMetrics(
  toolName: string,
  executor: () => Promise<any>
): Promise<any> {
  const startTime = Date.now();
  let success = true;

  try {
    const result = await executor();
    return result;
  } catch (error) {
    success = false;
    throw error;
  } finally {
    const duration = Date.now() - startTime;
    toolMetrics.recordCall(toolName, duration, success);
  }
}
```

## Next Steps

<CardGroup cols={2}>
  <Card title="Client API" href="/sdk/client" icon="code">
    Learn about the client interface and methods.
  </Card>

  <Card title="Examples" href="/examples/basic-chat" icon="book-open">
    See practical examples of tool integration.
  </Card>

  <Card title="Error Handling" href="/sdk/error-handling" icon="exclamation">
    Understand error handling and recovery.
  </Card>

  <Card title="API Reference" href="/sdk/api-reference" icon="laptop">
    Full API reference documentation.
  </Card>
</CardGroup>

{" "}
