TypeScriptADK-TS

Third-Party Tools

Integrate external APIs and libraries as ADK-TS tools

Integrate any external API, library, or service as an ADK-TS tool. Use createTool for simple integrations or BaseTool for complex tools with multiple operations.

You can wrap any JavaScript/TypeScript library, REST API, or external service as an ADK-TS tool.

Using createTool

Wrap external APIs or libraries with createTool for simple integrations. This approach is ideal for stateless operations and quick integrations.

REST API Integration

Integrate popular search APIs like Tavily and Serper by wrapping their REST endpoints:

import { createTool } from "@iqai/adk";
import { z } from "zod";

// Tavily Search API
const tavilySearch = createTool({
  name: "tavily_search",
  description: "Search the internet using Tavily",
  schema: z.object({
    query: z.string().describe("Search query"),
  }),
  fn: async ({ query }) => {
    const response = await fetch("https://api.tavily.com/search", {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        Authorization: `Bearer ${process.env.TAVILY_API_KEY}`,
      },
      body: JSON.stringify({ query }),
    });

    const data = await response.json();
    return data.results;
  },
});

// Serper Google Search
const serperSearch = createTool({
  name: "google_search",
  description: "Search Google using Serper API",
  schema: z.object({
    query: z.string(),
    num: z.number().optional().describe("Number of results (default: 10)"),
  }),
  fn: async ({ query, num = 10 }) => {
    const response = await fetch("https://google.serper.dev/search", {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        "X-API-KEY": process.env.SERPER_API_KEY!,
      },
      body: JSON.stringify({ q: query, num }),
    });

    const data = await response.json();
    return {
      organic: data.organic || [],
      answerBox: data.answerBox,
    };
  },
});

NPM Library Integration

Wrap existing NPM packages as ADK tools. This example shows how to use axios for generic HTTP requests:

import { createTool } from "@iqai/adk";
import { z } from "zod";
import axios from "axios";

// Wrap axios for HTTP requests
const httpRequest = createTool({
  name: "http_request",
  description: "Make HTTP requests to any URL",
  schema: z.object({
    url: z.string().url(),
    method: z.enum(["GET", "POST", "PUT", "DELETE"]).optional(),
    data: z.record(z.string(), z.unknown()).optional(),
  }),
  fn: async ({ url, method = "GET", data }) => {
    const response = await axios({ url, method, data });
    return response.data;
  },
});

Using BaseTool

Use BaseTool for complex integrations with stateful clients, connection pooling, or multiple operations. BaseTool gives you more control over the tool lifecycle and allows you to maintain persistent connections.

Stateful Client Tool

Create a tool that maintains a persistent database connection. The client is initialized once in the constructor and reused across multiple calls:

import { BaseTool, ToolContext, FunctionDeclaration } from "@iqai/adk";
import { Type } from "@google/genai";
import { Client } from "some-library";

class DatabaseTool extends BaseTool {
  private client: Client;

  constructor() {
    super({
      name: "query_database",
      description: "Query external database",
    });

    this.client = new Client({
      host: process.env.DB_HOST!,
      apiKey: process.env.DB_API_KEY!,
    });
  }

  async runAsync(
    args: Record<string, any>,
    context: ToolContext
  ): Promise<any> {
    try {
      const result = await this.client.query(args.sql, {
        limit: args.limit || 100,
      });
      return { rows: result.rows, count: result.count };
    } catch (error) {
      return {
        error: `Query failed: ${
          error instanceof Error ? error.message : "Unknown error"
        }`,
      };
    }
  }

  getDeclaration(): FunctionDeclaration {
    return {
      name: this.name,
      description: this.description,
      parameters: {
        type: Type.OBJECT,
        properties: {
          sql: { type: Type.STRING, description: "SQL query" },
          limit: { type: Type.NUMBER, description: "Max rows (default: 100)" },
        },
        required: ["sql"],
      },
    };
  }
}

Multi-Operation Tool

Create a tool that handles multiple related operations. This example shows a cloud storage tool that can upload, download, and list files:

import { BaseTool, ToolContext, FunctionDeclaration } from "@iqai/adk";
import { Type } from "@google/genai";
import { StorageClient } from "cloud-storage-library";

class StorageTool extends BaseTool {
  private storage: StorageClient;

  constructor() {
    super({
      name: "storage_operations",
      description: "Upload, download, or list files in cloud storage",
    });

    this.storage = new StorageClient({
      credentials: process.env.STORAGE_CREDENTIALS!,
    });
  }

  async runAsync(
    args: Record<string, any>,
    context: ToolContext
  ): Promise<any> {
    const { operation, bucket, path, content } = args;

    try {
      switch (operation) {
        case "upload":
          await this.storage.upload(bucket, path, content);
          return { success: true, path };

        case "download":
          const data = await this.storage.download(bucket, path);
          return { success: true, content: data };

        case "list":
          const files = await this.storage.list(bucket, path);
          return { success: true, files };

        default:
          return { error: `Unknown operation: ${operation}` };
      }
    } catch (error) {
      return {
        error: `Storage operation failed: ${
          error instanceof Error ? error.message : "Unknown"
        }`,
      };
    }
  }

  getDeclaration(): FunctionDeclaration {
    return {
      name: this.name,
      description: this.description,
      parameters: {
        type: Type.OBJECT,
        properties: {
          operation: {
            type: Type.STRING,
            description: "Operation: upload, download, or list",
            enum: ["upload", "download", "list"],
          },
          bucket: { type: Type.STRING, description: "Storage bucket name" },
          path: { type: Type.STRING, description: "File path" },
          content: {
            type: Type.STRING,
            description: "File content (for upload)",
          },
        },
        required: ["operation", "bucket", "path"],
      },
    };
  }
}

Error Handling

Handle errors gracefully in your third-party tool integrations. Always return error objects instead of throwing exceptions, and handle common scenarios like timeouts and authentication failures:

import { createTool } from "@iqai/adk";
import { z } from "zod";

const resilientApiCall = createTool({
  name: "api_call",
  description: "Call external API with error handling",
  schema: z.object({
    endpoint: z.string().url(),
  }),
  fn: async ({ endpoint }) => {
    try {
      const response = await fetch(endpoint, {
        headers: { Authorization: `Bearer ${process.env.API_KEY}` },
        signal: AbortSignal.timeout(10000), // 10s timeout
      });

      if (response.status === 401) {
        return {
          error: "Authentication failed",
          message: "Invalid API key or token",
        };
      }

      if (!response.ok) {
        return {
          error: `Request failed: ${response.status}`,
          message: await response.text(),
        };
      }

      return await response.json();
    } catch (error) {
      if (error instanceof Error && error.name === "TimeoutError") {
        return {
          error: "Request timed out",
          message: "The request took too long to complete",
          timestamp: new Date().toISOString(),
        };
      }
      return {
        error: "Service unavailable",
        message:
          error instanceof Error ? error.message : "Unknown error occurred",
        timestamp: new Date().toISOString(),
      };
    }
  },
});

Best Practices

Follow these guidelines when integrating third-party tools:

  • Use environment variables - Store API keys and credentials securely
  • Handle errors gracefully - Return useful error messages, don't throw exceptions
  • Set timeouts - Prevent tools from hanging indefinitely
  • Validate inputs - Use Zod schemas to validate parameters
  • Test thoroughly - Test your tools before using them with agents

How is this guide?