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

186 lines (185 loc) 6.75 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.validateCredentials = validateCredentials; exports.normalizePath = normalizePath; exports.appendQueryParams = appendQueryParams; exports.request = request; exports.get = get; exports.post = post; exports.put = put; exports.patch = patch; exports.del = del; const logger_util_js_1 = require("../utils/logger.util.js"); const transport_util_js_1 = require("../utils/transport.util.js"); const error_util_js_1 = require("../utils/error.util.js"); /** * @namespace VendorAtlassianApiService * @description Service layer for interacting with the Atlassian Confluence API. * Responsible for credentials validation, path normalization, * and making raw API requests via the transport utility. * * This service provides a thin wrapper around fetchAtlassian() to maintain * consistent layered architecture across all MCP servers: * - Transport (transport.util.ts): Raw HTTP operations * - Service (this file): API-specific logic, credentials, path handling * - Controller: Business logic, filtering, formatting */ // Create a contextualized logger for this file const serviceLogger = logger_util_js_1.Logger.forContext('services/vendor.atlassian.api.service.ts'); // Log service initialization serviceLogger.debug('Confluence API service initialized'); /** * Validates and returns Atlassian credentials * @throws {McpError} If credentials are missing * @returns {AtlassianCredentials} Valid credentials */ function validateCredentials() { const methodLogger = logger_util_js_1.Logger.forContext('services/vendor.atlassian.api.service.ts', 'validateCredentials'); const credentials = (0, transport_util_js_1.getAtlassianCredentials)(); if (!credentials) { methodLogger.error('Missing Atlassian credentials'); throw (0, error_util_js_1.createAuthMissingError)(); } methodLogger.debug('Credentials validated successfully'); return credentials; } /** * Normalizes the API path by ensuring it starts with / * @param path - The raw path provided by the user * @returns Normalized path */ function normalizePath(path) { let normalizedPath = path; if (!normalizedPath.startsWith('/')) { normalizedPath = '/' + normalizedPath; } return normalizedPath; } /** * Appends query parameters to a path * @param path - The base path * @param queryParams - Optional query parameters * @returns Path with query string appended */ function appendQueryParams(path, queryParams) { if (!queryParams || Object.keys(queryParams).length === 0) { return path; } const queryString = new URLSearchParams(queryParams).toString(); return path + (path.includes('?') ? '&' : '?') + queryString; } /** * Makes a generic API request to the Confluence API * * @param path - API endpoint path (e.g., '/wiki/api/v2/spaces') * @param options - Request options including method, queryParams, and body * @returns Promise resolving to the raw API response with rawResponsePath * @throws {McpError} If credentials are missing or API request fails * * @example * // GET request * const spaces = await request('/wiki/api/v2/spaces', { * method: 'GET', * queryParams: { limit: '10' } * }); * * @example * // POST request * const page = await request('/wiki/api/v2/pages', { * method: 'POST', * body: { spaceId: '123', title: 'New Page', ... } * }); */ async function request(path, options = {}) { const methodLogger = logger_util_js_1.Logger.forContext('services/vendor.atlassian.api.service.ts', 'request'); const method = options.method || 'GET'; methodLogger.debug(`Making ${method} request to ${path}`); try { // Validate credentials const credentials = validateCredentials(); // Normalize path and append query params let normalizedPath = normalizePath(path); normalizedPath = appendQueryParams(normalizedPath, options.queryParams); methodLogger.debug(`Normalized path: ${normalizedPath}`); // Prepare fetch options const fetchOptions = { method, }; // Add body for methods that support it if (options.body && ['POST', 'PUT', 'PATCH'].includes(method)) { fetchOptions.body = options.body; } // Make the API call const response = await (0, transport_util_js_1.fetchAtlassian)(credentials, normalizedPath, fetchOptions); methodLogger.debug('Successfully received response from Confluence API'); return response; } catch (error) { methodLogger.error(`Service error during ${method} request to ${path}`, error); // Rethrow McpErrors as-is if (error instanceof error_util_js_1.McpError) { throw error; } // This shouldn't happen as fetchAtlassian wraps all errors throw error; } } /** * Makes a GET request to the Confluence API * @param path - API endpoint path * @param queryParams - Optional query parameters * @returns Promise resolving to the API response with rawResponsePath */ async function get(path, queryParams) { return request(path, { method: 'GET', queryParams }); } /** * Makes a POST request to the Confluence API * @param path - API endpoint path * @param body - Request body * @param queryParams - Optional query parameters * @returns Promise resolving to the API response with rawResponsePath */ async function post(path, body, queryParams) { return request(path, { method: 'POST', body, queryParams }); } /** * Makes a PUT request to the Confluence API * @param path - API endpoint path * @param body - Request body * @param queryParams - Optional query parameters * @returns Promise resolving to the API response with rawResponsePath */ async function put(path, body, queryParams) { return request(path, { method: 'PUT', body, queryParams }); } /** * Makes a PATCH request to the Confluence API * @param path - API endpoint path * @param body - Request body * @param queryParams - Optional query parameters * @returns Promise resolving to the API response with rawResponsePath */ async function patch(path, body, queryParams) { return request(path, { method: 'PATCH', body, queryParams }); } /** * Makes a DELETE request to the Confluence API * @param path - API endpoint path * @param queryParams - Optional query parameters * @returns Promise resolving to the API response with rawResponsePath */ async function del(path, queryParams) { return request(path, { method: 'DELETE', queryParams }); } exports.default = { request, get, post, put, patch, del, validateCredentials, normalizePath, appendQueryParams, };