maplestory-mcp-server
Version:
Official-style NEXON MapleStory MCP Server for Claude Desktop - Complete character info, union details, guild data, rankings, and game mechanics
154 lines • 6.01 kB
JavaScript
;
/**
* Custom error classes for MCP Maple
* Provides structured error handling for different failure scenarios
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.TimeoutError = exports.NetworkError = exports.ValidationError = exports.RateLimitError = exports.InvalidApiKeyError = exports.GuildNotFoundError = exports.CharacterNotFoundError = exports.NexonApiError = exports.McpMapleError = void 0;
exports.createNexonApiError = createNexonApiError;
exports.isRetryableError = isRetryableError;
exports.getRetryDelay = getRetryDelay;
exports.sanitizeErrorForLogging = sanitizeErrorForLogging;
class McpMapleError extends Error {
code;
statusCode;
context;
constructor(message, code, statusCode, context) {
super(message);
this.code = code;
this.statusCode = statusCode;
this.context = context;
this.name = 'McpMapleError';
Object.setPrototypeOf(this, McpMapleError.prototype);
}
toJSON() {
return {
name: this.name,
message: this.message,
code: this.code,
statusCode: this.statusCode,
context: this.context,
stack: this.stack,
};
}
}
exports.McpMapleError = McpMapleError;
class NexonApiError extends McpMapleError {
constructor(message, statusCode, endpoint, params) {
super(message, 'NEXON_API_ERROR', statusCode, { endpoint, params });
this.name = 'NexonApiError';
Object.setPrototypeOf(this, NexonApiError.prototype);
}
}
exports.NexonApiError = NexonApiError;
class CharacterNotFoundError extends McpMapleError {
constructor(characterName) {
super(`Character '${characterName}' not found`, 'CHARACTER_NOT_FOUND', 404, { characterName });
this.name = 'CharacterNotFoundError';
Object.setPrototypeOf(this, CharacterNotFoundError.prototype);
}
}
exports.CharacterNotFoundError = CharacterNotFoundError;
class GuildNotFoundError extends McpMapleError {
constructor(guildName, worldName) {
super(`Guild '${guildName}' not found${worldName ? ` in world '${worldName}'` : ''}`, 'GUILD_NOT_FOUND', 404, { guildName, worldName });
this.name = 'GuildNotFoundError';
Object.setPrototypeOf(this, GuildNotFoundError.prototype);
}
}
exports.GuildNotFoundError = GuildNotFoundError;
class InvalidApiKeyError extends McpMapleError {
constructor() {
super('Invalid NEXON API key provided', 'INVALID_API_KEY', 401);
this.name = 'InvalidApiKeyError';
Object.setPrototypeOf(this, InvalidApiKeyError.prototype);
}
}
exports.InvalidApiKeyError = InvalidApiKeyError;
class RateLimitError extends McpMapleError {
constructor(retryAfter) {
super('API rate limit exceeded', 'RATE_LIMIT_EXCEEDED', 429, { retryAfter });
this.name = 'RateLimitError';
Object.setPrototypeOf(this, RateLimitError.prototype);
}
}
exports.RateLimitError = RateLimitError;
class ValidationError extends McpMapleError {
constructor(field, value, expectedType) {
super(`Validation failed for field '${field}': ${value}${expectedType ? ` (expected ${expectedType})` : ''}`, 'VALIDATION_ERROR', 400, { field, value, expectedType });
this.name = 'ValidationError';
Object.setPrototypeOf(this, ValidationError.prototype);
}
}
exports.ValidationError = ValidationError;
class NetworkError extends McpMapleError {
constructor(originalError) {
super('Network request failed', 'NETWORK_ERROR', undefined, {
originalError: originalError.message,
});
this.name = 'NetworkError';
Object.setPrototypeOf(this, NetworkError.prototype);
}
}
exports.NetworkError = NetworkError;
class TimeoutError extends McpMapleError {
constructor(timeout) {
super(`Request timed out after ${timeout}ms`, 'TIMEOUT_ERROR', 408, { timeout });
this.name = 'TimeoutError';
Object.setPrototypeOf(this, TimeoutError.prototype);
}
}
exports.TimeoutError = TimeoutError;
// Error factory functions
function createNexonApiError(statusCode, message, endpoint, params) {
switch (statusCode) {
case 401:
return new InvalidApiKeyError();
case 404:
if (endpoint?.includes('character')) {
const characterName = params?.character_name || 'unknown';
return new CharacterNotFoundError(characterName);
}
if (endpoint?.includes('guild')) {
const guildName = params?.guild_name || 'unknown';
const worldName = params?.world_name;
return new GuildNotFoundError(guildName, worldName);
}
return new McpMapleError(message, 'NOT_FOUND', statusCode, { endpoint, params });
case 429:
return new RateLimitError();
default:
return new NexonApiError(message, statusCode, endpoint, params);
}
}
// Error utilities
function isRetryableError(error) {
if (error instanceof NexonApiError) {
// Retry on server errors and rate limits (with backoff)
return error.statusCode ? error.statusCode >= 500 || error.statusCode === 429 : false;
}
if (error instanceof RateLimitError) {
return true;
}
if (error instanceof NetworkError || error instanceof TimeoutError) {
return true;
}
return false;
}
function getRetryDelay(attemptNumber, baseDelay = 1000) {
// Exponential backoff with jitter
const delay = baseDelay * Math.pow(2, attemptNumber - 1);
const jitter = Math.random() * 0.1 * delay;
return Math.min(delay + jitter, 30000); // Max 30 seconds
}
function sanitizeErrorForLogging(error) {
if (error instanceof McpMapleError) {
return error.toJSON();
}
return {
name: error.name || 'UnknownError',
message: error.message || 'Unknown error occurred',
stack: error.stack,
};
}
//# sourceMappingURL=errors.js.map