@tiflux/mcp
Version:
TiFlux MCP Server - Model Context Protocol integration for Claude Code and other AI clients
342 lines (308 loc) • 9.52 kB
JavaScript
/**
* Custom Error Classes
* Hierarquia de erros específicos do sistema TiFlux MCP
*/
/**
* Erro base do TiFlux MCP
*/
class TiFluxError extends Error {
constructor(message, code = 'TIFLUX_ERROR', statusCode = 500, details = {}) {
super(message);
this.name = this.constructor.name;
this.code = code;
this.statusCode = statusCode;
this.details = details;
this.timestamp = new Date().toISOString();
// Capturar stack trace
Error.captureStackTrace(this, this.constructor);
}
/**
* Converte erro para objeto JSON
* @returns {Object}
*/
toJSON() {
return {
name: this.name,
message: this.message,
code: this.code,
statusCode: this.statusCode,
details: this.details,
timestamp: this.timestamp,
stack: this.stack
};
}
/**
* Converte erro para formato MCP response
* @returns {Object}
*/
toMCPResponse() {
return {
content: [
{
type: 'text',
text: `**❌ ${this.name}**\n\n` +
`**Erro:** ${this.message}\n` +
`**Código:** ${this.code}\n` +
(Object.keys(this.details).length > 0 ?
`**Detalhes:** ${JSON.stringify(this.details, null, 2)}\n` : '') +
`**Timestamp:** ${this.timestamp}\n\n` +
`*Entre em contato com o suporte se o problema persistir.*`
}
]
};
}
}
/**
* Erro de validação de entrada
*/
class ValidationError extends TiFluxError {
constructor(message, field = null, value = null) {
super(message, 'VALIDATION_ERROR', 400, { field, value });
}
toMCPResponse() {
const fieldInfo = this.details.field ? `**Campo:** ${this.details.field}\n` : '';
const valueInfo = this.details.value !== null ? `**Valor:** ${this.details.value}\n` : '';
return {
content: [
{
type: 'text',
text: `**⚠️ Erro de Validação**\n\n` +
`**Mensagem:** ${this.message}\n` +
fieldInfo + valueInfo +
`\n*Verifique os parâmetros informados e tente novamente.*`
}
]
};
}
}
/**
* Erro de configuração
*/
class ConfigError extends TiFluxError {
constructor(message, configKey = null) {
super(message, 'CONFIG_ERROR', 500, { configKey });
}
toMCPResponse() {
return {
content: [
{
type: 'text',
text: `**⚙️ Erro de Configuração**\n\n` +
`**Problema:** ${this.message}\n` +
(this.details.configKey ? `**Configuração:** ${this.details.configKey}\n` : '') +
`\n*Verifique as configurações do sistema e variáveis de ambiente.*`
}
]
};
}
}
/**
* Erro de API externa (TiFlux API)
*/
class APIError extends TiFluxError {
constructor(message, statusCode = 500, responseData = null, endpoint = null) {
super(message, 'API_ERROR', statusCode, { responseData, endpoint });
}
toMCPResponse() {
const statusText = this.getStatusText(this.statusCode);
return {
content: [
{
type: 'text',
text: `**🌐 Erro da API TiFlux**\n\n` +
`**Status:** ${this.statusCode} ${statusText}\n` +
`**Mensagem:** ${this.message}\n` +
(this.details.endpoint ? `**Endpoint:** ${this.details.endpoint}\n` : '') +
`**Timestamp:** ${this.timestamp}\n\n` +
this.getSuggestion() +
`\n*Se o problema persistir, verifique o status da API TiFlux.*`
}
]
};
}
/**
* Obtém texto descritivo do status HTTP
* @param {number} status - Status code
* @returns {string}
*/
getStatusText(status) {
const statusTexts = {
400: 'Bad Request',
401: 'Unauthorized',
403: 'Forbidden',
404: 'Not Found',
422: 'Unprocessable Entity',
429: 'Too Many Requests',
500: 'Internal Server Error',
502: 'Bad Gateway',
503: 'Service Unavailable',
504: 'Gateway Timeout'
};
return statusTexts[status] || 'Unknown Status';
}
/**
* Obtém sugestão baseada no status code
* @returns {string}
*/
getSuggestion() {
switch (this.statusCode) {
case 401:
return '**Sugestão:** Verifique se a TIFLUX_API_KEY está configurada corretamente.';
case 403:
return '**Sugestão:** Você não tem permissão para acessar este recurso.';
case 404:
return '**Sugestão:** O recurso solicitado não foi encontrado. Verifique os IDs informados.';
case 422:
return '**Sugestão:** Verifique se todos os campos obrigatórios foram informados corretamente.';
case 429:
return '**Sugestão:** Muitas requisições. Aguarde alguns segundos antes de tentar novamente.';
case 500:
case 502:
case 503:
case 504:
return '**Sugestão:** Erro interno da API. Tente novamente em alguns minutos.';
default:
return '**Sugestão:** Verifique os parâmetros e tente novamente.';
}
}
}
/**
* Erro de timeout ou conexão
*/
class ConnectionError extends TiFluxError {
constructor(message, originalError = null) {
super(message, 'CONNECTION_ERROR', 503, { originalError: originalError?.message });
}
toMCPResponse() {
return {
content: [
{
type: 'text',
text: `**📡 Erro de Conexão**\n\n` +
`**Problema:** ${this.message}\n` +
`**Timestamp:** ${this.timestamp}\n\n` +
`**Possíveis causas:**\n` +
`• Problema de conectividade com a internet\n` +
`• Instabilidade na API TiFlux\n` +
`• Timeout da requisição\n\n` +
`**Sugestões:**\n` +
`• Verifique sua conexão com a internet\n` +
`• Tente novamente em alguns minutos\n` +
`• Contate o suporte se o problema persistir`
}
]
};
}
}
/**
* Erro de rate limiting
*/
class RateLimitError extends TiFluxError {
constructor(message, retryAfter = null) {
super(message, 'RATE_LIMIT_ERROR', 429, { retryAfter });
}
toMCPResponse() {
const retryInfo = this.details.retryAfter ?
`**Tente novamente após:** ${this.details.retryAfter} segundos\n` : '';
return {
content: [
{
type: 'text',
text: `**⏱️ Limite de Requisições Atingido**\n\n` +
`**Mensagem:** ${this.message}\n` +
retryInfo +
`**Timestamp:** ${this.timestamp}\n\n` +
`*Aguarde alguns segundos antes de fazer nova requisição.*`
}
]
};
}
}
/**
* Erro de autenticação
*/
class AuthenticationError extends TiFluxError {
constructor(message = 'Token de API inválido ou expirado') {
super(message, 'AUTH_ERROR', 401);
}
toMCPResponse() {
return {
content: [
{
type: 'text',
text: `**🔐 Erro de Autenticação**\n\n` +
`**Problema:** ${this.message}\n` +
`**Timestamp:** ${this.timestamp}\n\n` +
`**Como resolver:**\n` +
`1. Verifique se a variável TIFLUX_API_KEY está definida\n` +
`2. Confirme se o token está válido e não expirou\n` +
`3. Entre em contato com o administrador para obter novo token\n\n` +
`*Configure a variável de ambiente: TIFLUX_API_KEY=seu_token_aqui*`
}
]
};
}
}
/**
* Erro de recurso não encontrado
*/
class NotFoundError extends TiFluxError {
constructor(resource, identifier = null) {
const message = identifier ?
`${resource} com ID '${identifier}' não encontrado` :
`${resource} não encontrado`;
super(message, 'NOT_FOUND_ERROR', 404, { resource, identifier });
}
toMCPResponse() {
return {
content: [
{
type: 'text',
text: `**🔍 Recurso Não Encontrado**\n\n` +
`**Recurso:** ${this.details.resource}\n` +
(this.details.identifier ? `**Identificador:** ${this.details.identifier}\n` : '') +
`**Mensagem:** ${this.message}\n\n` +
`**Sugestões:**\n` +
`• Verifique se o ID/identificador está correto\n` +
`• Confirme se você tem permissão para acessar o recurso\n` +
`• Use a ferramenta de busca para localizar o recurso correto`
}
]
};
}
}
/**
* Factory para criar erros baseados em responses HTTP
* @param {Object} response - Response da API
* @param {string} endpoint - Endpoint que falhou
* @returns {TiFluxError} - Erro apropriado
*/
function createErrorFromResponse(response, endpoint = null) {
const { status, error } = response;
switch (status) {
case 401:
return new AuthenticationError(error);
case 404:
return new NotFoundError('Recurso', null);
case 422:
return new ValidationError(error);
case 429:
return new RateLimitError(error);
case 'CONNECTION_ERROR':
case 'PARSE_ERROR':
return new ConnectionError(error);
default:
return new APIError(error, status, null, endpoint);
}
}
module.exports = {
TiFluxError,
ValidationError,
ConfigError,
APIError,
ConnectionError,
RateLimitError,
AuthenticationError,
NotFoundError,
createErrorFromResponse
};