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

201 lines (200 loc) 10.5 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const logger_util_js_1 = require("../utils/logger.util.js"); const error_util_js_1 = require("../utils/error.util.js"); const error_handler_util_js_1 = require("../utils/error-handler.util.js"); const vendor_atlassian_spaces_service_js_1 = __importDefault(require("../services/vendor.atlassian.spaces.service.js")); const vendor_atlassian_pages_service_js_1 = __importDefault(require("../services/vendor.atlassian.pages.service.js")); const atlassian_spaces_formatter_js_1 = require("../controllers/atlassian.spaces.formatter.js"); const atlassian_pages_controller_js_1 = __importDefault(require("./atlassian.pages.controller.js")); const defaults_util_js_1 = require("../utils/defaults.util.js"); const pagination_util_js_1 = require("../utils/pagination.util.js"); const formatter_util_js_1 = require("../utils/formatter.util.js"); /** * Controller for managing Confluence spaces. * Provides functionality for listing spaces and retrieving space details. */ /** * List Confluence spaces with optional filtering * @param options - Options for filtering spaces * @param options.type - Filter by space type (global, personal, etc.) * @param options.status - Filter by space status (current, archived) * @param options.limit - Maximum number of spaces to return * @param options.cursor - Pagination cursor for subsequent requests * @returns Promise with formatted spaces list content including pagination information * @throws Error if space listing fails */ async function list(options = {}) { const controllerLogger = logger_util_js_1.Logger.forContext('controllers/atlassian.spaces.controller.ts', 'list'); controllerLogger.debug('Listing Confluence spaces with options:', options); try { // Create defaults object with proper typing const defaults = { limit: defaults_util_js_1.DEFAULT_PAGE_SIZE, type: 'global', // Add default that matches CLI option status: 'current', // Add default that matches CLI option }; // Apply defaults const mergedOptions = (0, defaults_util_js_1.applyDefaults)(options, defaults); // Map controller options to service parameters const params = { // Pass keys if provided keys: mergedOptions.keys, // Convert 'archived' type to actual API parameters - the tool uses a simplified schema type: mergedOptions.type === 'archived' ? 'global' : mergedOptions.type, status: mergedOptions.status, limit: mergedOptions.limit, cursor: mergedOptions.cursor, // Additional parameters descriptionFormat: 'view', includeIcon: true, // Include space icons in response // Add sort parameter for the service sort: '-name', // Default sort order }; controllerLogger.debug('Using params:', params); const spacesData = await vendor_atlassian_spaces_service_js_1.default.list(params); // Log only summary information controllerLogger.debug(`Retrieved ${spacesData.results.length} spaces. Has more: ${spacesData._links?.next ? 'yes' : 'no'}`); // Extract pagination info using the utility const pagination = (0, pagination_util_js_1.extractPaginationInfo)(spacesData, pagination_util_js_1.PaginationType.CURSOR, 'Space'); // Format the spaces data using the formatter const formattedSpaces = (0, atlassian_spaces_formatter_js_1.formatSpacesList)(spacesData); // Create the complete content string by appending the pagination information let finalContent = formattedSpaces; // Only add pagination information if it exists and contains relevant information if (pagination && (pagination.hasMore || pagination.count !== undefined)) { const paginationString = (0, formatter_util_js_1.formatPagination)(pagination); finalContent += '\n\n' + paginationString; } return { content: finalContent, }; } catch (error) { // Use the standardized error handler throw (0, error_handler_util_js_1.handleControllerError)(error, { entityType: 'Spaces', operation: 'listing', source: 'controllers/atlassian.spaces.controller.ts@list', }); } } /** * Get details of a specific Confluence space * @param args - Object containing the key of the space to retrieve * @param args.spaceKey - The key of the space * @returns Promise with formatted space details content * @throws Error if space retrieval fails */ async function get(args) { const { spaceKey } = args; const controllerLogger = logger_util_js_1.Logger.forContext('controllers/atlassian.spaces.controller.ts', 'get'); controllerLogger.debug(`Getting Confluence space with key: ${spaceKey}...`); try { // Create defaults object with proper typing for space details const defaults = { descriptionFormat: 'view', includeIcon: false, includeLabels: true, includeOperations: false, includePermissions: defaults_util_js_1.SPACE_DEFAULTS.INCLUDE_PERMISSIONS, includeRoleAssignments: false, }; // Hardcoded parameters for the service call - use defaults const params = defaults; controllerLogger.debug('Using params:', params); // The Confluence API v2 doesn't provide a direct endpoint to get full space details by key. // It only allows retrieving spaces by ID for detailed information. Therefore, we must: // 1. Use the list endpoint with keys filter to find the space ID first // 2. Use the get endpoint with the ID to retrieve complete space details // This two-step lookup is necessary due to API constraints. controllerLogger.debug('Searching for space by key'); const spacesResponse = await vendor_atlassian_spaces_service_js_1.default.list({ keys: [spaceKey], limit: 1, ...params, }); // Check if space was found if (!spacesResponse.results || spacesResponse.results.length === 0) { throw (0, error_util_js_1.createApiError)(`Space not found with key: ${spaceKey}. Verify the space key is correct and that you have access to this space.`, 404); } // Get the space from the results const spaceId = spacesResponse.results[0].id; // Get full space details using the ID const spaceData = await vendor_atlassian_spaces_service_js_1.default.get(spaceId, params); // Log only key information instead of the entire response controllerLogger.debug(`Retrieved space: ${spaceData.name} (${spaceData.id})`); // Get homepage content if available let homepageContent = ''; if (spaceData.homepageId) { try { controllerLogger.debug(`Fetching homepage content for ID: ${spaceData.homepageId}`); const homepageResult = await atlassian_pages_controller_js_1.default.get({ pageId: spaceData.homepageId, }); // Extract content from the homepage result const content = homepageResult.content; // Look for the Content section or any main content const contentMatch = content.match(/## Content\n([\s\S]*?)(?=\n## |$)/) || content.match(/# .*?\n([\s\S]*?)(?=\n# |$)/); if (contentMatch && contentMatch[1]) { homepageContent = contentMatch[1].trim(); controllerLogger.debug('Successfully extracted homepage content section'); } else { // If no specific content section found, use everything after the title const titleMatch = content.match(/# .*?\n([\s\S]*)/); if (titleMatch && titleMatch[1]) { homepageContent = titleMatch[1].trim(); controllerLogger.debug('Extracted homepage content from title section'); } else { controllerLogger.debug('No content sections found in homepage'); } } } catch (error) { controllerLogger.warn(`Failed to fetch homepage content: ${error instanceof Error ? error.message : String(error)}`); homepageContent = '*Failed to retrieve homepage content. The page may be inaccessible or deleted.*'; } } // Fetch top-level pages in the space to include in the response let topLevelPagesData = null; try { controllerLogger.debug(`Fetching top pages for space ID: ${spaceId}`); topLevelPagesData = await vendor_atlassian_pages_service_js_1.default.list({ spaceId: [spaceId], limit: 5, // Limit to 5 top-level pages sort: '-modified-date', // Sort by most recently modified status: ['current'], // Only show current (not archived/deleted) pages }); controllerLogger.debug(`Retrieved ${topLevelPagesData.results.length} top-level pages for the space`); } catch (error) { controllerLogger.warn(`Failed to fetch top-level pages: ${error instanceof Error ? error.message : String(error)}`); // Continue even if we couldn't get the pages } // Format the space data for display with homepage content using the formatter const formattedSpace = (0, atlassian_spaces_formatter_js_1.formatSpaceDetails)(spaceData, homepageContent, topLevelPagesData); return { content: formattedSpace, }; } catch (error) { // Use the standardized error handler throw (0, error_handler_util_js_1.handleControllerError)(error, { entityType: 'Space', entityId: spaceKey, operation: 'retrieving', source: 'controllers/atlassian.spaces.controller.ts@get', }); } } exports.default = { list, get };