Skip to main content
Glama
agent-client.ts7.58 kB
/** * ByteBot Agent API HTTP Client * * Provides HTTP client for interacting with ByteBot Agent API (task management) * Includes error handling, retry logic, and request/response validation */ import axios, { AxiosInstance } from 'axios'; import { Task, CreateTaskRequest, CreateTaskResponse, UpdateTaskRequest, ListTasksQuery, } from '../types/bytebot.js'; import { EnvironmentConfig, TaskCacheEntry } from '../types/mcp.js'; import { handleAxiosError, withRetry, logError, } from '../utils/error-handler.js'; import { validateTaskId, CreateTaskSchema, UpdateTaskSchema, ListTasksSchema, } from '../utils/validators.js'; /** * Agent API Client */ export class AgentClient { private client: AxiosInstance; private config: EnvironmentConfig; private taskCache: Map<string, TaskCacheEntry> = new Map(); private readonly CACHE_TTL = 5000; // 5 seconds constructor(config: EnvironmentConfig) { this.config = config; this.client = axios.create({ baseURL: config.agentUrl, timeout: config.requestTimeout, headers: { 'Content-Type': 'application/json', }, }); // Log client initialization console.log(`[ByteBot MCP] Agent API client initialized: ${config.agentUrl}`); } /** * Create a new task */ async createTask(request: CreateTaskRequest): Promise<CreateTaskResponse> { try { // Validate request const validatedRequest = CreateTaskSchema.parse(request); // Make request with retry logic const response = await withRetry( async () => { return await this.client.post<CreateTaskResponse>( '/tasks', validatedRequest ); }, { maxRetries: this.config.maxRetries, delay: this.config.retryDelay, backoffMultiplier: 2, maxDelay: 10000, } ); console.log( `[ByteBot MCP] Task created successfully: ${response.data.id}` ); return response.data; } catch (error) { logError('createTask', error); throw handleAxiosError(error); } } /** * List all tasks with optional filters */ async listTasks(query?: ListTasksQuery): Promise<Task[]> { try { // Validate query parameters if (query) { ListTasksSchema.parse(query); } const response = await withRetry( async () => { return await this.client.get<Task[]>('/tasks', { params: query, }); }, { maxRetries: this.config.maxRetries, delay: this.config.retryDelay, backoffMultiplier: 2, maxDelay: 10000, } ); console.log(`[ByteBot MCP] Retrieved ${response.data.length} tasks`); return response.data; } catch (error) { logError('listTasks', error); throw handleAxiosError(error); } } /** * Get a specific task by ID */ async getTask(taskId: string, useCache = true): Promise<Task> { try { validateTaskId(taskId); // Check cache first if (useCache) { const cached = this.getCachedTask(taskId); if (cached) { console.log(`[ByteBot MCP] Returning cached task: ${taskId}`); return cached; } } const response = await withRetry( async () => { return await this.client.get<Task>(`/tasks/${taskId}`); }, { maxRetries: this.config.maxRetries, delay: this.config.retryDelay, backoffMultiplier: 2, maxDelay: 10000, } ); // Cache the task this.cacheTask(taskId, response.data); console.log( `[ByteBot MCP] Retrieved task ${taskId}: status=${response.data.status}` ); return response.data; } catch (error) { logError('getTask', error); throw handleAxiosError(error); } } /** * Get the currently in-progress task */ async getInProgressTask(): Promise<Task | null> { try { const response = await withRetry( async () => { return await this.client.get<Task>('/tasks/in-progress'); }, { maxRetries: this.config.maxRetries, delay: this.config.retryDelay, backoffMultiplier: 2, maxDelay: 10000, } ); if (response.data) { console.log( `[ByteBot MCP] Retrieved in-progress task: ${response.data.id}` ); this.cacheTask(response.data.id, response.data); return response.data; } console.log(`[ByteBot MCP] No task currently in progress`); return null; } catch (error) { // 404 is expected when no task is in progress const bytebotError = handleAxiosError(error); if (bytebotError.statusCode === 404) { return null; } logError('getInProgressTask', error); throw bytebotError; } } /** * Update a task */ async updateTask(taskId: string, update: UpdateTaskRequest): Promise<Task> { try { validateTaskId(taskId); // Validate update request const validatedUpdate = UpdateTaskSchema.parse(update); const response = await withRetry( async () => { return await this.client.patch<Task>( `/tasks/${taskId}`, validatedUpdate ); }, { maxRetries: this.config.maxRetries, delay: this.config.retryDelay, backoffMultiplier: 2, maxDelay: 10000, } ); // Invalidate cache this.invalidateCache(taskId); console.log( `[ByteBot MCP] Task ${taskId} updated: status=${response.data.status}` ); return response.data; } catch (error) { logError('updateTask', error); throw handleAxiosError(error); } } /** * Delete a task */ async deleteTask(taskId: string): Promise<void> { try { validateTaskId(taskId); await withRetry( async () => { return await this.client.delete(`/tasks/${taskId}`); }, { maxRetries: this.config.maxRetries, delay: this.config.retryDelay, backoffMultiplier: 2, maxDelay: 10000, } ); // Invalidate cache this.invalidateCache(taskId); console.log(`[ByteBot MCP] Task ${taskId} deleted successfully`); } catch (error) { logError('deleteTask', error); throw handleAxiosError(error); } } /** * Health check */ async healthCheck(): Promise<boolean> { try { await this.client.get('/tasks', { timeout: 5000 }); return true; } catch (error) { console.error('[ByteBot MCP] Agent API health check failed:', error); return false; } } /** * Cache management */ private getCachedTask(taskId: string): Task | null { const entry = this.taskCache.get(taskId); if (!entry) return null; const now = Date.now(); if (now - entry.lastFetched > entry.ttl) { // Cache expired this.taskCache.delete(taskId); return null; } return entry.task; } private cacheTask(taskId: string, task: Task): void { this.taskCache.set(taskId, { task, lastFetched: Date.now(), ttl: this.CACHE_TTL, }); } private invalidateCache(taskId: string): void { this.taskCache.delete(taskId); } /** * Clear all cache */ clearCache(): void { this.taskCache.clear(); console.log('[ByteBot MCP] Task cache cleared'); } }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/sensuslab/spark-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server