breathe-api
Version:
Model Context Protocol server for Breathe HR APIs with Swagger/OpenAPI support - also works with custom APIs
187 lines • 6.88 kB
JavaScript
export class BaseError extends Error {
code;
context;
suggestions;
timestamp;
isRetryable;
constructor(message, code, context = {}, suggestions = [], isRetryable = false) {
super(message);
this.name = this.constructor.name;
this.code = code;
this.context = context;
this.suggestions = suggestions;
this.timestamp = new Date();
this.isRetryable = isRetryable;
if (Error.captureStackTrace) {
Error.captureStackTrace(this, this.constructor);
}
}
getUserMessage() {
let message = this.message;
if (this.suggestions.length > 0) {
message += '\n\nSuggestions:';
this.suggestions.forEach((suggestion, index) => {
message += `\n${index + 1}. ${suggestion.message}`;
if (suggestion.action) {
message += ` (${suggestion.action})`;
}
});
}
return message;
}
toJSON() {
return {
name: this.name,
code: this.code,
message: this.message,
context: this.context,
suggestions: this.suggestions,
timestamp: this.timestamp,
isRetryable: this.isRetryable,
stack: this.stack,
};
}
}
export class ApiError extends BaseError {
statusCode;
response;
constructor(message, statusCode, response, context = {}, suggestions = []) {
const code = `API_ERROR_${statusCode || 'UNKNOWN'}`;
const isRetryable = statusCode ? [408, 429, 500, 502, 503, 504].includes(statusCode) : false;
super(message, code, { ...context, statusCode, response }, suggestions, isRetryable);
this.statusCode = statusCode;
this.response = response;
}
static fromAxiosError(error) {
const statusCode = error.response?.status;
const response = error.response?.data;
const url = error.config?.url;
let message = error.message;
const suggestions = [];
switch (statusCode) {
case 401:
message = `Authentication failed for ${url}`;
suggestions.push({
message: 'Check your API credentials',
action: 'Verify BREATHE_API_USERNAME and BREATHE_API_PASSWORD environment variables',
});
break;
case 403:
message = `Access forbidden for ${url}`;
suggestions.push({
message: 'Ensure your API user has the necessary permissions',
action: 'Contact your Breathe HR administrator',
});
break;
case 404:
message = `Resource not found: ${url}`;
suggestions.push({
message: 'Verify the API endpoint URL is correct',
action: 'Check the API documentation for the correct endpoint',
});
break;
case 429:
message = `Rate limit exceeded for ${url}`;
suggestions.push({
message: 'Wait before retrying',
action: 'Implement exponential backoff or reduce request frequency',
});
break;
case 500:
case 502:
case 503:
message = `Server error from ${url}`;
suggestions.push({
message: 'The API server is experiencing issues',
action: 'Try again in a few moments',
});
break;
}
return new ApiError(message, statusCode, response, {
url,
method: error.config?.method,
headers: error.config?.headers,
}, suggestions);
}
}
export class ValidationError extends BaseError {
field;
value;
constructor(message, field, value, suggestions = []) {
super(message, 'VALIDATION_ERROR', { field, value }, suggestions, false);
this.field = field;
this.value = value;
}
}
export class ConfigurationError extends BaseError {
constructor(message, configKey, suggestions = []) {
super(message, 'CONFIGURATION_ERROR', { configKey }, suggestions, false);
}
}
export class SwaggerParseError extends BaseError {
constructor(message, swaggerUrl, parseError, suggestions = []) {
if (suggestions.length === 0) {
suggestions.push({
message: 'Ensure the Swagger/OpenAPI specification is valid',
action: 'Validate the spec at https://editor.swagger.io',
});
}
super(message, 'SWAGGER_PARSE_ERROR', { swaggerUrl, parseError }, suggestions, true);
}
}
export class CodeGenerationError extends BaseError {
constructor(message, platform, context = {}, suggestions = []) {
super(message, 'CODE_GENERATION_ERROR', { ...context, platform }, suggestions, false);
}
}
export class TimeoutError extends BaseError {
constructor(message, operation, timeoutMs, suggestions = []) {
if (suggestions.length === 0) {
suggestions.push({
message: 'The operation took too long to complete',
action: 'Try again or increase the timeout limit',
});
}
super(message, 'TIMEOUT_ERROR', { operation, timeoutMs }, suggestions, true);
}
}
export class CircuitBreakerError extends BaseError {
constructor(message, service, failureCount, suggestions = []) {
if (suggestions.length === 0) {
suggestions.push({
message: `The ${service} service is temporarily unavailable due to repeated failures`,
action: 'Wait a few minutes before trying again',
});
}
super(message, 'CIRCUIT_BREAKER_OPEN', { service, failureCount }, suggestions, true);
}
}
export function wrapError(error, context = {}, suggestions = []) {
if (error instanceof BaseError) {
Object.assign(error.context, context);
error.suggestions.push(...suggestions);
return error;
}
if (error instanceof Error) {
return new BaseError(error.message, 'WRAPPED_ERROR', { ...context, originalError: error.name }, suggestions, false);
}
return new BaseError(String(error), 'UNKNOWN_ERROR', context, suggestions, false);
}
export function formatErrorForMcp(error) {
if (error instanceof BaseError) {
return {
message: error.getUserMessage(),
code: error.code,
context: error.context,
};
}
if (error instanceof Error) {
return {
message: error.message,
};
}
return {
message: String(error),
};
}
//# sourceMappingURL=errors.js.map