New protocol version released: This page may contain outdated information.
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:// 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:// 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: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:// 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:// 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
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
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
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: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: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
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
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
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
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
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
Client API
Learn about the client interface and methods.
Examples
See practical examples of tool integration.
Error Handling
Understand error handling and recovery.
API Reference
Full API reference documentation.