@pluggedin/pluggedin-mcp-proxy
Version:
Unified MCP proxy that aggregates all your MCP servers (STDIO, SSE, Streamable HTTP) into one powerful interface. Access any tool through a single connection, search across unified documents with built-in RAG, and receive notifications from any model. Tes
114 lines (113 loc) • 3.87 kB
JavaScript
import axios from 'axios';
import { getPluggedinMCPApiKey, getPluggedinMCPApiBaseUrl } from './utils.js';
import { debugLog, debugError } from './debug-log.js';
/**
* Creates a configured axios instance with standard headers and error handling
*/
export function createHttpClient() {
const apiKey = getPluggedinMCPApiKey();
const baseUrl = getPluggedinMCPApiBaseUrl();
const client = axios.create({
baseURL: baseUrl,
timeout: 30000, // 30 seconds default timeout
headers: {
'Content-Type': 'application/json',
...(apiKey && { 'Authorization': `Bearer ${apiKey}` })
}
});
// Request interceptor for logging
client.interceptors.request.use((config) => {
debugLog(`[HTTP] ${config.method?.toUpperCase()} ${config.url}`, {
headers: config.headers,
params: config.params
});
return config;
}, (error) => {
debugError('[HTTP] Request error:', error);
return Promise.reject(error);
});
// Response interceptor for logging and error handling
client.interceptors.response.use((response) => {
debugLog(`[HTTP] Response ${response.status} from ${response.config.url}`);
return response;
}, (error) => {
debugError(`[HTTP] Response error:`, {
status: error.response?.status,
url: error.config?.url,
message: error.message
});
return Promise.reject(error);
});
return client;
}
/**
* Makes an authenticated API request with standard error handling
*/
export async function makeApiRequest(config) {
const { requiresAuth = true, ...axiosConfig } = config;
if (requiresAuth && !getPluggedinMCPApiKey()) {
throw new Error('API key is required for this operation');
}
const client = createHttpClient();
try {
const response = await client.request(axiosConfig);
return response.data;
}
catch (error) {
if (axios.isAxiosError(error)) {
// Enhance error messages based on status codes
const status = error.response?.status;
const enhancedMessage = getEnhancedErrorMessage(status, error.message);
throw new Error(enhancedMessage);
}
throw error;
}
}
/**
* Provides user-friendly error messages based on HTTP status codes
*/
function getEnhancedErrorMessage(status, defaultMessage) {
switch (status) {
case 400:
return 'Invalid request data provided';
case 401:
return 'Invalid API key or authentication failed';
case 403:
return 'Permission denied - check your account status';
case 404:
return 'Resource not found';
case 413:
return 'Request payload too large';
case 429:
return 'Rate limit exceeded - please try again later';
case 500:
return 'Server error - please try again later';
case 502:
case 503:
case 504:
return 'Service temporarily unavailable - please try again later';
default:
return defaultMessage;
}
}
/**
* Helper to build URL with query parameters
*/
export function buildUrl(path, params) {
const baseUrl = getPluggedinMCPApiBaseUrl();
const url = new URL(path, baseUrl);
if (params) {
Object.entries(params).forEach(([key, value]) => {
if (value !== undefined && value !== null) {
// Convert booleans to 1/0 for backend compatibility
if (typeof value === 'boolean') {
url.searchParams.append(key, value ? '1' : '0');
}
else {
url.searchParams.append(key, String(value));
}
}
});
}
return url.toString();
}