Skip to main content
Glama
rest-client.ts6.92 kB
import axios, { AxiosInstance, AxiosResponse } from "axios"; import { Config, RequestOptions, ApiResponse } from "./types.js"; import { AuthManager } from "./auth.js"; import { SwaggerDocumentationParser } from "./swagger.js"; export class RestClient { private httpClient: AxiosInstance; private authManager: AuthManager; private swaggerParser?: SwaggerDocumentationParser; private config: Config; constructor(config: Config) { this.config = config; this.httpClient = axios.create({ baseURL: config.baseUrl, timeout: config.timeout, }); this.authManager = new AuthManager( config.auth, config.baseUrl, config.timeout ); if (config.swaggerUrl) { this.swaggerParser = new SwaggerDocumentationParser(config.swaggerUrl); } this.setupInterceptors(); } private setupInterceptors(): void { // Request interceptor to add authentication headers this.httpClient.interceptors.request.use( async (config) => { await this.authManager.ensureAuthenticated(); const authHeaders = this.authManager.getAuthHeaders(); Object.assign(config.headers, authHeaders); return config; }, (error) => Promise.reject(error) ); // Response interceptor for error handling and retry logic this.httpClient.interceptors.response.use( (response) => response, async (error) => { if (error.response?.status === 401) { // Token might be expired, try to re-authenticate this.authManager.logout(); await this.authManager.authenticate(); // Retry the original request const originalRequest = error.config; if (!originalRequest._retry) { originalRequest._retry = true; const authHeaders = this.authManager.getAuthHeaders(); Object.assign(originalRequest.headers, authHeaders); return this.httpClient(originalRequest); } } return Promise.reject(error); } ); } async initialize(): Promise<void> { await this.authManager.authenticate(); if (this.swaggerParser) { try { console.log( `🔍 Attempting to parse Swagger documentation from: ${this.config.swaggerUrl}` ); await this.swaggerParser.parseDocumentation(); console.log("✅ Swagger documentation parsed successfully"); } catch (error) { console.error( "❌ Failed to parse Swagger documentation:", error instanceof Error ? error.message : "Unknown error" ); // Don't throw the error, just log it so the client can still work without Swagger // this.swaggerParser = undefined; // Optionally disable swagger parser } } } async makeRequest<T = any>(options: RequestOptions): Promise<ApiResponse<T>> { const { method, path, params, body, headers } = options; try { let response: AxiosResponse<T>; const config = { headers: headers || {}, params: method === "GET" ? params : undefined, }; switch (method) { case "GET": response = await this.httpClient.get(path, config); break; case "POST": response = await this.httpClient.post(path, body || params, config); break; case "PUT": response = await this.httpClient.put(path, body || params, config); break; case "DELETE": response = await this.httpClient.delete(path, config); break; case "PATCH": response = await this.httpClient.patch(path, body || params, config); break; default: throw new Error(`Unsupported HTTP method: ${method}`); } return { data: response.data, status: response.status, statusText: response.statusText, headers: response.headers as Record<string, string>, }; } catch (error) { if (axios.isAxiosError(error)) { throw new Error( `HTTP ${error.response?.status || "Unknown"}: ${ error.response?.data?.message || error.message }` ); } throw error; } } async get<T = any>( path: string, params?: Record<string, any>, headers?: Record<string, string> ): Promise<ApiResponse<T>> { return this.makeRequest<T>({ method: "GET", path, params, headers }); } async post<T = any>( path: string, body?: any, headers?: Record<string, string> ): Promise<ApiResponse<T>> { return this.makeRequest<T>({ method: "POST", path, body, headers }); } async put<T = any>( path: string, body?: any, headers?: Record<string, string> ): Promise<ApiResponse<T>> { return this.makeRequest<T>({ method: "PUT", path, body, headers }); } async delete<T = any>( path: string, headers?: Record<string, string> ): Promise<ApiResponse<T>> { return this.makeRequest<T>({ method: "DELETE", path, headers }); } async patch<T = any>( path: string, body?: any, headers?: Record<string, string> ): Promise<ApiResponse<T>> { return this.makeRequest<T>({ method: "PATCH", path, body, headers }); } getSwaggerDocumentation(): string { if (!this.swaggerParser) { return "Swagger documentation not configured"; } return this.swaggerParser.getEndpointSummary(); } searchEndpoints(query: string): string { if (!this.swaggerParser) { return "Swagger documentation not configured"; } const endpoints = this.swaggerParser.searchEndpoints(query); if (endpoints.length === 0) { return `No endpoints found matching "${query}"`; } return endpoints .map( (endpoint) => `${endpoint.method} ${endpoint.path}${ endpoint.summary ? ` - ${endpoint.summary}` : "" }` ) .join("\n"); } getEndpointInfo(path: string, method: string): string { if (!this.swaggerParser) { return "Swagger documentation not configured"; } const endpoint = this.swaggerParser.getEndpoint(path, method); if (!endpoint) { return `Endpoint ${method} ${path} not found`; } let info = `${endpoint.method} ${endpoint.path}\n`; if (endpoint.summary) info += `Summary: ${endpoint.summary}\n`; if (endpoint.description) info += `Description: ${endpoint.description}\n`; if (endpoint.parameters && endpoint.parameters.length > 0) { info += "\nParameters:\n"; endpoint.parameters.forEach((param) => { info += ` - ${param.name} (${param.in})${ param.required ? " *required*" : "" }: ${param.description || "No description"}\n`; }); } return info; } isAuthenticated(): boolean { return this.authManager.isAuthenticated(); } async logout(): Promise<void> { this.authManager.logout(); } }

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/gyuco/mcp-http-client'

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