Skip to main content
Glama
by wsapi-chat
index.ts7.08 kB
import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios'; import { config } from '../config/index.js'; import { createLogger, logRequest } from '../utils/logger.js'; import { handleError, withRetry } from '../utils/errors.js'; import type { paths } from '../types/api.js'; const logger = createLogger('wsapi-client'); // Type helpers for API operations type ApiPaths = paths; type GetPaths = { [K in keyof ApiPaths]: ApiPaths[K] extends { get: any } ? K : never; }[keyof ApiPaths]; type PostPaths = { [K in keyof ApiPaths]: ApiPaths[K] extends { post: any } ? K : never; }[keyof ApiPaths]; type PutPaths = { [K in keyof ApiPaths]: ApiPaths[K] extends { put: any } ? K : never; }[keyof ApiPaths]; type DeletePaths = { [K in keyof ApiPaths]: ApiPaths[K] extends { delete: any } ? K : never; }[keyof ApiPaths]; // Request/Response type helpers type GetRequest<T extends GetPaths> = ApiPaths[T]['get'] extends { parameters: any } ? ApiPaths[T]['get']['parameters'] : {}; type GetResponse<T extends GetPaths> = ApiPaths[T]['get'] extends { responses: any } ? ApiPaths[T]['get']['responses'][200] extends { content: { 'application/json': infer U } } ? U : any : any; type PostRequest<T extends PostPaths> = ApiPaths[T]['post'] extends { requestBody: any } ? ApiPaths[T]['post']['requestBody']['content']['application/json'] : {}; type PostResponse<T extends PostPaths> = ApiPaths[T]['post'] extends { responses: any } ? ApiPaths[T]['post']['responses'][201] extends { content: { 'application/json': infer U } } ? U : ApiPaths[T]['post']['responses'][200] extends { content: { 'application/json': infer U } } ? U : any : any; type PutRequest<T extends PutPaths> = ApiPaths[T]['put'] extends { requestBody: any } ? ApiPaths[T]['put']['requestBody']['content']['application/json'] : {}; type PutResponse<T extends PutPaths> = ApiPaths[T]['put'] extends { responses: any } ? ApiPaths[T]['put']['responses'][200] extends { content: { 'application/json': infer U } } ? U : any : any; export class WSAPIClient { private readonly client: AxiosInstance; constructor() { this.client = axios.create({ baseURL: config.wsapi.baseUrl, timeout: config.wsapi.timeout, headers: { 'Content-Type': 'application/json', 'X-Api-Key': config.wsapi.apiKey, 'X-Instance-Id': config.wsapi.instanceId, 'User-Agent': 'WSAPI-MCP-Server/1.0.0', }, }); this.setupInterceptors(); } private setupInterceptors(): void { // Request interceptor this.client.interceptors.request.use( (config) => { const startTime = Date.now(); (config as any).metadata = { startTime }; logger.debug('Making API request', { method: config.method?.toUpperCase(), url: config.url, headers: { ...config.headers, 'X-Api-Key': '[REDACTED]' }, }); return config; }, (error) => { logger.error('Request interceptor error', { error: error.message }); return Promise.reject(error); } ); // Response interceptor this.client.interceptors.response.use( (response: AxiosResponse & { config: AxiosRequestConfig & { metadata?: { startTime: number } } }) => { const duration = response.config.metadata?.startTime ? Date.now() - response.config.metadata.startTime : undefined; logRequest( response.config.method?.toUpperCase() || 'UNKNOWN', response.config.url || 'UNKNOWN', response.status, duration ); logger.debug('API request successful', { method: response.config.method?.toUpperCase(), url: response.config.url, status: response.status, duration, }); return response; }, (error) => { const duration = error.config?.metadata?.startTime ? Date.now() - error.config.metadata.startTime : undefined; logRequest( error.config?.method?.toUpperCase() || 'UNKNOWN', error.config?.url || 'UNKNOWN', error.response?.status, duration ); return Promise.reject(error); } ); } // Generic request method with retry logic private async request<T>(requestConfig: AxiosRequestConfig): Promise<T> { return withRetry( async () => { try { const response = await this.client.request<T>(requestConfig); return response.data; } catch (error) { throw handleError(error, { method: requestConfig.method, url: requestConfig.url, }); } }, { maxAttempts: config.wsapi.retryAttempts, delay: config.wsapi.retryDelay, retryableErrors: ['NETWORK_ERROR', 'INSTANCE_ERROR'], } ); } // GET request async get(path: string, params?: any): Promise<any> { const url = this.buildUrl(path, params); return this.request<any>({ method: 'GET', url, }); } // POST request async post(path: string, data: any, params?: any): Promise<any> { const url = this.buildUrl(path, params); return this.request<any>({ method: 'POST', url, data, }); } // PUT request async put(path: string, data: any, params?: any): Promise<any> { const url = this.buildUrl(path, params); return this.request<any>({ method: 'PUT', url, data, }); } // DELETE request async delete(path: string, params?: any): Promise<void> { const url = this.buildUrl(path, params); return this.request<void>({ method: 'DELETE', url, }); } // Helper to build URL with path parameters private buildUrl(path: string, params?: any): string { let url = path; if (params) { // Replace path parameters Object.entries(params).forEach(([key, value]) => { if (typeof value === 'string' || typeof value === 'number') { url = url.replace(`{${key}}`, encodeURIComponent(value.toString())); } }); // Add query parameters const queryParams = new URLSearchParams(); Object.entries(params).forEach(([key, value]) => { if (typeof value === 'string' || typeof value === 'number') { if (!path.includes(`{${key}}`)) { queryParams.append(key, value.toString()); } } }); if (queryParams.toString()) { url += `?${queryParams.toString()}`; } } return url; } // Health check method async healthCheck(): Promise<boolean> { try { await this.get('/session/status'); return true; } catch (error) { logger.warn('Health check failed', { error: (error as Error).message }); return false; } } // Get instance info async getInstanceInfo(): Promise<any> { return this.get('/instance/settings'); } } // Export singleton instance export const wsapiClient = new WSAPIClient(); export default WSAPIClient;

Latest Blog Posts

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/wsapi-chat/wsapi-mcp'

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