OpenAPI Tools
Create tools for REST API integration using OpenAPI specifications
OpenAPI tools enable integration with REST APIs based on OpenAPI specifications. While ADK TypeScript doesn't currently include automatic tool generation from OpenAPI specs, you can manually create API tools using the existing HttpRequestTool and FunctionTool components.
Current Implementation Approach
ADK TypeScript provides foundational tools for API integration that can be combined to work with OpenAPI-documented services.
Available Components
🌐 HttpRequestTool
Make HTTP requests to REST API endpoints
🔧 FunctionTool
Create custom wrappers for specific API operations
🔐 Authentication
Handle API authentication and credentials
Future Enhancement
Automatic OpenAPI tool generation is planned for a future release. The current approach requires manual tool creation but provides full flexibility and control.
Manual API Tool Creation
Using HttpRequestTool
The HttpRequestTool provides a general-purpose solution for API integration:
import { HttpRequestTool, LlmAgent } from '@iqai/adk';
const httpTool = new HttpRequestTool();
const apiAgent = new LlmAgent({
name: 'api-agent',
model: 'gemini-1.5-flash',
description: 'Agent that can interact with REST APIs',
instruction: `
You can make HTTP requests to APIs using the http_request tool.
When calling APIs:
- Use the correct HTTP method (GET, POST, PUT, DELETE)
- Include required headers and authentication
- Format request bodies as JSON strings when needed
- Parse and interpret the response data
`,
tools: [httpTool]
});
Creating Specific API Tools
For better control and LLM understanding, create specific tools for individual API operations:
import { FunctionTool } from '@iqai/adk';
// Create a specific tool for weather API
async function getWeather(location: string, apiKey: string) {
const response = await fetch(
`https://api.openweathermap.org/data/2.5/weather?q=${location}&appid=${apiKey}`
);
if (!response.ok) {
return {
error: `Weather API error: ${response.status} ${response.statusText}`
};
}
const data = await response.json();
return {
location: data.name,
temperature: Math.round(data.main.temp - 273.15), // Convert from Kelvin
description: data.weather[0].description,
humidity: data.main.humidity
};
}
const weatherTool = new FunctionTool(getWeather, {
name: 'get_weather',
description: 'Get current weather information for a specific location'
});
API Wrapper Class Pattern
For complex APIs, create wrapper classes that encapsulate multiple operations:
import { BaseTool } from '@iqai/adk';
interface ApiConfig {
baseUrl: string;
apiKey: string;
timeout?: number;
}
class ApiTool extends BaseTool {
private config: ApiConfig;
constructor(config: ApiConfig) {
super({
name: 'api_tool',
description: 'Custom API integration tool'
});
this.config = config;
}
async runAsync(args: {
operation: string;
path: string;
method?: string;
body?: string;
headers?: Record<string, string>;
}) {
try {
const url = `${this.config.baseUrl}${args.path}`;
const headers = {
'Authorization': `Bearer ${this.config.apiKey}`,
'Content-Type': 'application/json',
...args.headers
};
const response = await fetch(url, {
method: args.method || 'GET',
headers,
body: args.body,
signal: AbortSignal.timeout(this.config.timeout || 10000)
});
const responseData = await response.text();
return {
status: response.status,
statusText: response.statusText,
headers: Object.fromEntries(response.headers.entries()),
data: responseData
};
} catch (error) {
return {
error: `API request failed: ${error instanceof Error ? error.message : String(error)}`
};
}
}
getDeclaration() {
return {
name: this.name,
description: this.description,
parameters: {
type: 'object',
properties: {
operation: {
type: 'string',
description: 'The API operation to perform'
},
path: {
type: 'string',
description: 'API endpoint path'
},
method: {
type: 'string',
description: 'HTTP method (GET, POST, PUT, DELETE)',
enum: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH']
},
body: {
type: 'string',
description: 'Request body as JSON string'
},
headers: {
type: 'object',
description: 'Additional request headers'
}
},
required: ['operation', 'path']
}
};
}
}
OpenAPI Specification Guidance
Reading OpenAPI Specs
When working with OpenAPI specifications manually:
- Identify Endpoints: Review the
paths
object for available operations - Understand Parameters: Check path, query, header, and body parameters
- Review Authentication: Check
securitySchemes
for auth requirements - Study Responses: Understand expected response formats and status codes
Implementation Strategy
- Start Simple: Begin with basic GET operations using HttpRequestTool
- Add Authentication: Implement required auth patterns
- Create Specific Tools: Build dedicated tools for frequently used operations
- Error Handling: Implement robust error handling and validation
- Testing: Thoroughly test all API interactions
Authentication Patterns
API Key Authentication
async function callApiWithKey(endpoint: string, apiKey: string, params?: Record<string, string>) {
const url = new URL(endpoint);
if (params) {
Object.entries(params).forEach(([key, value]) => {
url.searchParams.append(key, value);
});
}
const response = await fetch(url.toString(), {
headers: {
'X-API-Key': apiKey,
'Content-Type': 'application/json'
}
});
return response.json();
}
Bearer Token Authentication
async function callApiWithToken(endpoint: string, token: string, data?: any) {
const response = await fetch(endpoint, {
method: data ? 'POST' : 'GET',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
},
body: data ? JSON.stringify(data) : undefined
});
return response.json();
}
Error Handling Best Practices
Response Validation
async function safeApiCall(url: string, options: RequestInit = {}) {
try {
const response = await fetch(url, options);
if (!response.ok) {
return {
error: `HTTP ${response.status}: ${response.statusText}`,
status: response.status
};
}
const contentType = response.headers.get('content-type');
if (contentType?.includes('application/json')) {
const data = await response.json();
return { data, status: response.status };
} else {
const text = await response.text();
return { data: text, status: response.status };
}
} catch (error) {
return {
error: `Network error: ${error instanceof Error ? error.message : String(error)}`
};
}
}
Retry Logic
async function apiCallWithRetry(
url: string,
options: RequestInit = {},
maxRetries = 3
) {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
const response = await fetch(url, options);
if (response.ok || response.status < 500) {
return response;
}
} catch (error) {
if (attempt === maxRetries) {
throw error;
}
// Exponential backoff
await new Promise(resolve => setTimeout(resolve, Math.pow(2, attempt) * 1000));
}
}
}
Future Roadmap
Planned Features
Future versions of ADK TypeScript will include:
- Automatic tool generation from OpenAPI specifications
- Built-in OpenAPI parser and validator
- Advanced authentication flow handling
- Dynamic tool registration and discovery
Expected Features
- OpenAPIToolset Class: Automatic tool generation from specifications
- Schema Validation: Built-in request/response validation
- Operation Discovery: Automatic endpoint discovery and mapping
- Auth Integration: Seamless authentication handling
- Type Generation: TypeScript type generation from schemas
Best Practices
Tool Design
- Single Responsibility: Create focused tools for specific operations
- Clear Descriptions: Provide detailed tool descriptions for LLM understanding
- Error Handling: Implement comprehensive error handling
- Parameter Validation: Validate inputs before making API calls
Agent Instructions
- API Documentation: Provide clear instructions about API usage
- Error Recovery: Define how to handle API failures
- Rate Limiting: Inform agents about API rate limits
- Data Formats: Explain expected request and response formats
Security Considerations
- Credential Management: Store API keys and tokens securely
- Environment Variables: Use environment variables for sensitive data
- Access Controls: Implement appropriate access restrictions
- Audit Logging: Log API usage for security and compliance