UNPKG

@kya-os/mcp-i

Version:

COMING SOON:Production-ready MCP Identity with automatic registration, key rotation, and optimized performance

340 lines 14.4 kB
/** * Transport abstraction for HTTP requests * Supports both axios and native fetch with runtime detection */ /** * Native fetch-based transport (preferred for Edge runtimes) */ export class FetchTransport { async post(url, data, options) { const controller = new AbortController(); const timeout = options?.timeout || 30000; const timeoutId = setTimeout(() => controller.abort(), timeout); try { const response = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json', ...options?.headers }, body: JSON.stringify(data), signal: controller.signal }); clearTimeout(timeoutId); if (!response.ok) { let errorMessage = ''; let errorData = null; try { // Try to parse JSON error response errorData = await response.json(); // Handle CLI endpoint error format with suggestions if (errorData.suggestion) { errorMessage = `${errorData.error || errorData.message || 'Request failed'}. ${errorData.suggestion}`; } else { errorMessage = errorData.message || errorData.error || ''; } } catch { // Fallback to text if not JSON errorMessage = await response.text(); } // Handle specific error cases if (response.status === 429) { throw new Error('Rate limit exceeded. Please wait a few minutes and try again.'); } else if (response.status === 403) { throw new Error('Access forbidden. Please check your API credentials.'); } else if (response.status === 404) { throw new Error('API endpoint not found. The service might be unavailable.'); } else if (response.status === 500 || response.status === 502 || response.status === 503) { throw new Error('Server error during registration. This might be a temporary issue. Please try again in a few moments.'); } else if (response.status === 400) { throw new Error(`Bad request: ${errorMessage || 'Invalid registration data'}`); } // Generic error with status code throw new Error(`HTTP ${response.status}: ${errorMessage || 'Request failed'}`); } const responseData = await response.json(); return { data: responseData, status: response.status, headers: Object.fromEntries(response.headers.entries()) }; } catch (error) { clearTimeout(timeoutId); if (error.name === 'AbortError') { throw new Error('Request timeout. The server is taking too long to respond. Please try again.'); } throw error; } } async get(url, options) { const controller = new AbortController(); const timeout = options?.timeout || 30000; const timeoutId = setTimeout(() => controller.abort(), timeout); try { const response = await fetch(url, { method: 'GET', headers: options?.headers, signal: controller.signal }); clearTimeout(timeoutId); if (!response.ok) { let errorMessage = ''; let errorData = null; try { // Try to parse JSON error response errorData = await response.json(); // Handle CLI endpoint error format with suggestions if (errorData.suggestion) { errorMessage = `${errorData.error || errorData.message || 'Request failed'}. ${errorData.suggestion}`; } else { errorMessage = errorData.message || errorData.error || ''; } } catch { // Fallback to text if not JSON errorMessage = await response.text(); } // Handle specific error cases if (response.status === 429) { throw new Error('Rate limit exceeded. Please wait a few minutes and try again.'); } else if (response.status === 403) { throw new Error('Access forbidden. Please check your API credentials.'); } else if (response.status === 404) { throw new Error('API endpoint not found. The service might be unavailable.'); } else if (response.status === 500 || response.status === 502 || response.status === 503) { throw new Error('Server error during registration. This might be a temporary issue. Please try again in a few moments.'); } else if (response.status === 400) { throw new Error(`Bad request: ${errorMessage || 'Invalid registration data'}`); } // Generic error with status code throw new Error(`HTTP ${response.status}: ${errorMessage || 'Request failed'}`); } const responseData = await response.json(); return { data: responseData, status: response.status, headers: Object.fromEntries(response.headers.entries()) }; } catch (error) { clearTimeout(timeoutId); if (error.name === 'AbortError') { throw new Error('Request timeout. The server is taking too long to respond. Please try again.'); } throw error; } } } /** * Axios-based transport (for Node.js environments) */ export class AxiosTransport { axiosInstance; async post(url, data, options) { const axios = await this.getAxios(); try { const response = await axios.post(url, data, { timeout: options?.timeout || 30000, headers: options?.headers }); return { data: response.data, status: response.status, headers: response.headers }; } catch (error) { // Handle specific Axios error cases if (error.response) { const status = error.response.status; let errorMessage = ''; // Handle CLI endpoint error format with suggestions if (error.response.data?.suggestion) { errorMessage = `${error.response.data.error || error.response.data.message || 'Request failed'}. ${error.response.data.suggestion}`; } else { errorMessage = error.response.data?.message || error.response.data?.error || ''; } if (status === 429) { throw new Error('Rate limit exceeded. Please wait a few minutes and try again.'); } else if (status === 403) { throw new Error('Access forbidden. Please check your API credentials.'); } else if (status === 404) { throw new Error('API endpoint not found. The service might be unavailable.'); } else if (status === 500 || status === 502 || status === 503) { throw new Error('Server error during registration. This might be a temporary issue. Please try again in a few moments.'); } else if (status === 400) { throw new Error(`Bad request: ${errorMessage || 'Invalid registration data'}`); } // Generic error with status code throw new Error(`HTTP ${status}: ${errorMessage || 'Request failed'}`); } else if (error.request) { // Request was made but no response received throw new Error('No response from server. Please check your internet connection.'); } else { // Something else happened throw new Error(error.message || 'Request failed'); } } } async get(url, options) { const axios = await this.getAxios(); try { const response = await axios.get(url, { timeout: options?.timeout || 30000, headers: options?.headers }); return { data: response.data, status: response.status, headers: response.headers }; } catch (error) { // Handle specific Axios error cases if (error.response) { const status = error.response.status; let errorMessage = ''; // Handle CLI endpoint error format with suggestions if (error.response.data?.suggestion) { errorMessage = `${error.response.data.error || error.response.data.message || 'Request failed'}. ${error.response.data.suggestion}`; } else { errorMessage = error.response.data?.message || error.response.data?.error || ''; } if (status === 429) { throw new Error('Rate limit exceeded. Please wait a few minutes and try again.'); } else if (status === 403) { throw new Error('Access forbidden. Please check your API credentials.'); } else if (status === 404) { throw new Error('API endpoint not found. The service might be unavailable.'); } else if (status === 500 || status === 502 || status === 503) { throw new Error('Server error. This might be a temporary issue. Please try again in a few moments.'); } else if (status === 400) { throw new Error(`Bad request: ${errorMessage || 'Invalid request data'}`); } // Generic error with status code throw new Error(`HTTP ${status}: ${errorMessage || 'Request failed'}`); } else if (error.request) { // Request was made but no response received throw new Error('No response from server. Please check your internet connection.'); } else { // Something else happened throw new Error(error.message || 'Request failed'); } } } async getAxios() { if (!this.axiosInstance) { try { // Try to require axios - if it fails, we'll throw a helpful error // @ts-ignore - dynamic require for optional dependency const axios = require('axios'); this.axiosInstance = axios.default || axios; } catch (error) { throw new Error('axios is not installed. Please install it with: npm install axios\n' + 'Or use the fetch transport instead by setting transport: "fetch" in options'); } } return this.axiosInstance; } } /** * Runtime detection for optimal transport selection */ export class RuntimeDetector { static isEdgeRuntime() { return !!(globalThis.EdgeRuntime || process.env.NEXT_RUNTIME === 'edge' || process.env.VERCEL_EDGE || globalThis.Deno || globalThis.Bun); } static isNodeRuntime() { return !!(process.versions?.node && !this.isEdgeRuntime() && !this.isNextJs()); } static isNextJs() { return !!(process.env.NEXT_RUNTIME || globalThis.__NEXT_DATA__ || process.env.NEXT_PUBLIC_VERCEL_ENV); } static hasFetch() { return typeof globalThis.fetch === 'function'; } static isLambda() { return !!(process.env.AWS_LAMBDA_FUNCTION_NAME || process.env.LAMBDA_TASK_ROOT || process.env._HANDLER); } } /** * Transport factory with runtime detection */ export class TransportFactory { static create(options) { const transportType = options?.transport || 'auto'; if (transportType === 'fetch') { if (!RuntimeDetector.hasFetch()) { throw new Error('Fetch is not available in this runtime'); } return new FetchTransport(); } if (transportType === 'axios') { return new AxiosTransport(); } // Auto-detect based on runtime if (transportType === 'auto') { // Prefer fetch for edge runtimes if (RuntimeDetector.isEdgeRuntime() && RuntimeDetector.hasFetch()) { return new FetchTransport(); } // Use fetch for Next.js to avoid bundling issues if (RuntimeDetector.isNextJs() && RuntimeDetector.hasFetch()) { return new FetchTransport(); } // Use fetch if available and we're in a Lambda (lighter weight) if (RuntimeDetector.isLambda() && RuntimeDetector.hasFetch()) { return new FetchTransport(); } // Default to axios for Node.js if (RuntimeDetector.isNodeRuntime()) { return new AxiosTransport(); } // Fallback to fetch if available if (RuntimeDetector.hasFetch()) { return new FetchTransport(); } // Last resort: axios return new AxiosTransport(); } throw new Error(`Unknown transport type: ${transportType}`); } } //# sourceMappingURL=transport.js.map