UNPKG

@aashari/mcp-server-atlassian-confluence

Version:

Node.js/TypeScript MCP server for Atlassian Confluence. Provides tools enabling AI systems (LLMs) to list/get spaces & pages (content formatted as Markdown) and search via CQL. Connects AI seamlessly to Confluence knowledge bases using the standard MCP in

249 lines (248 loc) 9.13 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.McpError = exports.ErrorType = void 0; exports.getDeepOriginalError = getDeepOriginalError; exports.createAuthMissingError = createAuthMissingError; exports.createAuthInvalidError = createAuthInvalidError; exports.createApiError = createApiError; exports.createNotFoundError = createNotFoundError; exports.createUnexpectedError = createUnexpectedError; exports.ensureMcpError = ensureMcpError; exports.formatErrorForMcpTool = formatErrorForMcpTool; exports.formatErrorForMcpResource = formatErrorForMcpResource; exports.handleCliError = handleCliError; const logger_util_js_1 = require("./logger.util.js"); /** * Format a simple separator line */ function formatSeparator() { return '---'; } /** * Error types for classification */ var ErrorType; (function (ErrorType) { ErrorType["AUTH_MISSING"] = "AUTH_MISSING"; ErrorType["AUTH_INVALID"] = "AUTH_INVALID"; ErrorType["API_ERROR"] = "API_ERROR"; ErrorType["NOT_FOUND"] = "NOT_FOUND"; ErrorType["UNEXPECTED_ERROR"] = "UNEXPECTED_ERROR"; ErrorType["VALIDATION_ERROR"] = "VALIDATION_ERROR"; })(ErrorType || (exports.ErrorType = ErrorType = {})); /** * Custom error class with type classification */ class McpError extends Error { constructor(message, type, statusCode, originalError) { super(message); this.name = 'McpError'; this.type = type; this.statusCode = statusCode; this.originalError = originalError; } } exports.McpError = McpError; /** * Helper to unwrap nested McpErrors and return the deepest original error. * This is useful when an McpError contains another McpError as `originalError` * which in turn may wrap the vendor (Confluence) error text or object. */ function getDeepOriginalError(error) { if (!error) { return error; } let current = error; let depth = 0; const maxDepth = 10; // Prevent infinite recursion while (depth < maxDepth && current instanceof Error && 'originalError' in current && current.originalError) { current = current.originalError; depth++; } return current; } /** * Create an authentication missing error */ function createAuthMissingError(message = 'Authentication credentials are missing', originalError) { return new McpError(message, ErrorType.AUTH_MISSING, undefined, originalError); } /** * Create an authentication invalid error */ function createAuthInvalidError(message = 'Authentication credentials are invalid', originalError) { return new McpError(message, ErrorType.AUTH_INVALID, 401, originalError); } /** * Create an API error */ function createApiError(message, statusCode, originalError) { return new McpError(message, ErrorType.API_ERROR, statusCode, originalError); } /** * Create a not found error */ function createNotFoundError(message = 'Resource not found', originalError) { return new McpError(message, ErrorType.NOT_FOUND, 404, originalError); } /** * Create an unexpected error */ function createUnexpectedError(message = 'An unexpected error occurred', originalError) { return new McpError(message, ErrorType.UNEXPECTED_ERROR, undefined, originalError); } /** * Ensure an error is an McpError */ function ensureMcpError(error) { if (error instanceof McpError) { return error; } if (error instanceof Error) { return createUnexpectedError(error.message, error); } return createUnexpectedError(String(error)); } /** * Format error for MCP tool response with vendor detail for AI. */ function formatErrorForMcpTool(error) { const methodLogger = logger_util_js_1.Logger.forContext('utils/error.util.ts', 'formatErrorForMcpTool'); const mcpError = ensureMcpError(error); methodLogger.error(`${mcpError.type} error`, mcpError); // Get the deep original error for additional context const originalError = getDeepOriginalError(mcpError.originalError); // Create a detailed message including the original error information let detailedMessage = `Error: ${mcpError.message}`; // Safely extract details from the original error const errorDetails = originalError instanceof Error ? { message: originalError.message } : originalError; // Add Confluence-specific error handling for display if (originalError) { let vendorText = ''; if (originalError instanceof Error) { vendorText = originalError.message; } else if (typeof originalError === 'object') { vendorText = JSON.stringify(originalError); } else { vendorText = String(originalError); } if (!detailedMessage.includes(vendorText)) { detailedMessage += `\nConfluence API Error: ${vendorText}`; } } return { content: [{ type: 'text', text: detailedMessage }], metadata: { errorType: mcpError.type, statusCode: mcpError.statusCode, errorDetails, }, }; } /** * Format error for MCP resource response */ function formatErrorForMcpResource(error, uri) { const methodLogger = logger_util_js_1.Logger.forContext('utils/error.util.ts', 'formatErrorForMcpResource'); const mcpError = ensureMcpError(error); methodLogger.error(`${mcpError.type} error`, mcpError); return { contents: [ { uri, text: `Error: ${mcpError.message}`, mimeType: 'text/plain', description: `Error: ${mcpError.type}`, }, ], }; } /** * Handle error in CLI context */ function handleCliError(error) { const methodLogger = logger_util_js_1.Logger.forContext('utils/error.util.ts', 'handleCliError'); const mcpError = ensureMcpError(error); // Log detailed information at different levels based on error type if (mcpError.statusCode && mcpError.statusCode >= 500) { methodLogger.error(`${mcpError.type} error occurred`, { message: mcpError.message, statusCode: mcpError.statusCode, stack: mcpError.stack, }); } else { methodLogger.warn(`${mcpError.type} error occurred`, { message: mcpError.message, statusCode: mcpError.statusCode, }); } // Log additional debug information if DEBUG is enabled methodLogger.debug('Error details', { type: mcpError.type, statusCode: mcpError.statusCode, originalError: mcpError.originalError, stack: mcpError.stack, }); // Build structured CLI output const cliLines = []; cliLines.push(`❌ ${mcpError.message}`); if (mcpError.statusCode) { cliLines.push(`HTTP Status: ${mcpError.statusCode}`); } // Provide helpful context based on error type if (mcpError.type === ErrorType.AUTH_MISSING) { cliLines.push('\nTip: Make sure to set up your Atlassian credentials in the environment variables:'); cliLines.push('- ATLASSIAN_SITE_NAME'); cliLines.push('- ATLASSIAN_USER_EMAIL'); cliLines.push('- ATLASSIAN_API_TOKEN'); } else if (mcpError.type === ErrorType.AUTH_INVALID) { cliLines.push('\nTip: Check that your Atlassian API token is correct and has not expired.'); cliLines.push('Verify your user has the required permissions to access the requested resource.'); } else if (mcpError.type === ErrorType.API_ERROR && mcpError.statusCode === 429) { cliLines.push('\nTip: You may have exceeded Confluence API rate limits. Try again later or space out your requests.'); } else if (mcpError.type === ErrorType.NOT_FOUND) { cliLines.push('\nTip: Verify the resource ID or key is correct and that you have access to view this resource.'); cliLines.push('If you are using a space key, make sure it is spelled correctly (including case).'); } else if (mcpError.type === ErrorType.VALIDATION_ERROR && mcpError.message.includes('CQL')) { cliLines.push('\nTip: Check your CQL syntax. Refer to Confluence Query Language documentation for valid queries.'); } cliLines.push(formatSeparator()); // Include the deep original error details const deepOriginal = getDeepOriginalError(mcpError.originalError); if (deepOriginal) { cliLines.push('Confluence API Error:'); let vendorText = ''; if (deepOriginal instanceof Error) { vendorText = deepOriginal.message; } else if (typeof deepOriginal === 'object') { vendorText = JSON.stringify(deepOriginal, null, 2); } else { vendorText = String(deepOriginal); } cliLines.push('```json'); cliLines.push(vendorText.trim()); cliLines.push('```'); } // Display DEBUG tip if (!process.env.DEBUG) { cliLines.push('\nFor detailed error information, run with DEBUG=mcp:* environment variable.'); } console.error(cliLines.join('\n')); process.exit(1); }