Skip to main content
Glama
vendor.atlassian.api.service.ts7.21 kB
import { Logger } from '../utils/logger.util.js'; import { fetchAtlassian, getAtlassianCredentials, AtlassianCredentials, TransportResponse, } from '../utils/transport.util.js'; import { createAuthMissingError, McpError } from '../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.forContext( 'services/vendor.atlassian.api.service.ts', ); // Log service initialization serviceLogger.debug('Confluence API service initialized'); /** * Supported HTTP methods for API requests */ export type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE'; /** * Request options for API calls */ export interface ApiRequestOptions { method?: HttpMethod; queryParams?: Record<string, string>; body?: Record<string, unknown>; } /** * Validates and returns Atlassian credentials * @throws {McpError} If credentials are missing * @returns {AtlassianCredentials} Valid credentials */ export function validateCredentials(): AtlassianCredentials { const methodLogger = Logger.forContext( 'services/vendor.atlassian.api.service.ts', 'validateCredentials', ); const credentials = getAtlassianCredentials(); if (!credentials) { methodLogger.error('Missing Atlassian credentials'); throw 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 */ export function normalizePath(path: string): string { 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 */ export function appendQueryParams( path: string, queryParams?: Record<string, string>, ): string { 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', ... } * }); */ export async function request<T = unknown>( path: string, options: ApiRequestOptions = {}, ): Promise<TransportResponse<T>> { const methodLogger = 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: HttpMethod; body?: unknown; } = { 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 fetchAtlassian<T>( 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 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 */ export async function get<T = unknown>( path: string, queryParams?: Record<string, string>, ): Promise<TransportResponse<T>> { return request<T>(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 */ export async function post<T = unknown>( path: string, body?: Record<string, unknown>, queryParams?: Record<string, string>, ): Promise<TransportResponse<T>> { return request<T>(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 */ export async function put<T = unknown>( path: string, body?: Record<string, unknown>, queryParams?: Record<string, string>, ): Promise<TransportResponse<T>> { return request<T>(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 */ export async function patch<T = unknown>( path: string, body?: Record<string, unknown>, queryParams?: Record<string, string>, ): Promise<TransportResponse<T>> { return request<T>(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 */ export async function del<T = unknown>( path: string, queryParams?: Record<string, string>, ): Promise<TransportResponse<T>> { return request<T>(path, { method: 'DELETE', queryParams }); } export default { request, get, post, put, patch, del, validateCredentials, normalizePath, appendQueryParams, };

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/aashari/mcp-server-atlassian-confluence'

If you have feedback or need assistance with the MCP directory API, please join our Discord server