Skip to main content
Glama
atlassian.api.controller.ts5.6 kB
import { fetchAtlassian, getAtlassianCredentials, } from '../utils/transport.util.js'; import { Logger } from '../utils/logger.util.js'; import { handleControllerError } from '../utils/error-handler.util.js'; import { ControllerResponse } from '../types/common.types.js'; import { GetApiToolArgsType, RequestWithBodyArgsType, } from '../tools/atlassian.api.types.js'; import { applyJqFilter, toOutputString } from '../utils/jq.util.js'; import { createAuthMissingError } from '../utils/error.util.js'; // Logger instance for this module const logger = Logger.forContext('controllers/atlassian.api.controller.ts'); /** * Supported HTTP methods for API requests */ type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE'; /** * Output format type */ type OutputFormat = 'toon' | 'json'; /** * Base options for all API requests */ interface BaseRequestOptions { path: string; queryParams?: Record<string, string>; jq?: string; outputFormat?: OutputFormat; } /** * Options for requests that include a body (POST, PUT, PATCH) */ interface RequestWithBodyOptions extends BaseRequestOptions { body?: Record<string, unknown>; } /** * Normalizes the API path by ensuring it starts with / * @param path - The raw path provided by the user * @returns Normalized path */ 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 */ 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; } /** * Shared handler for all HTTP methods * * @param method - HTTP method (GET, POST, PUT, PATCH, DELETE) * @param options - Request options including path, queryParams, body (for non-GET), and jq filter * @returns Promise with raw JSON response (optionally filtered) */ async function handleRequest( method: HttpMethod, options: RequestWithBodyOptions, ): Promise<ControllerResponse> { const methodLogger = logger.forMethod(`handle${method}`); try { methodLogger.debug(`Making ${method} request`, { path: options.path, ...(options.body && { bodyKeys: Object.keys(options.body) }), }); // Get credentials const credentials = getAtlassianCredentials(); if (!credentials) { throw createAuthMissingError(); } // Normalize path and append query params let path = normalizePath(options.path); path = appendQueryParams(path, options.queryParams); methodLogger.debug(`${method}ing: ${path}`); const fetchOptions: { method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE'; body?: unknown; } = { method, }; // Add body for methods that support it if (options.body && ['POST', 'PUT', 'PATCH'].includes(method)) { fetchOptions.body = options.body; } const response = await fetchAtlassian<unknown>( credentials, path, fetchOptions, ); methodLogger.debug('Successfully received response'); // Apply JQ filter if provided, otherwise return raw data const result = applyJqFilter(response, options.jq); // Convert to output format (TOON by default, JSON if requested) const useToon = options.outputFormat !== 'json'; const content = await toOutputString(result, useToon); return { content, }; } catch (error) { throw handleControllerError(error, { entityType: 'API', operation: `${method} request`, source: `controllers/atlassian.api.controller.ts@handle${method}`, additionalInfo: { path: options.path }, }); } } /** * Generic GET request to Confluence API * * @param options - Options containing path, queryParams, and optional jq filter * @returns Promise with raw JSON response (optionally filtered) */ export async function handleGet( options: GetApiToolArgsType, ): Promise<ControllerResponse> { return handleRequest('GET', options); } /** * Generic POST request to Confluence API * * @param options - Options containing path, body, queryParams, and optional jq filter * @returns Promise with raw JSON response (optionally filtered) */ export async function handlePost( options: RequestWithBodyArgsType, ): Promise<ControllerResponse> { return handleRequest('POST', options); } /** * Generic PUT request to Confluence API * * @param options - Options containing path, body, queryParams, and optional jq filter * @returns Promise with raw JSON response (optionally filtered) */ export async function handlePut( options: RequestWithBodyArgsType, ): Promise<ControllerResponse> { return handleRequest('PUT', options); } /** * Generic PATCH request to Confluence API * * @param options - Options containing path, body, queryParams, and optional jq filter * @returns Promise with raw JSON response (optionally filtered) */ export async function handlePatch( options: RequestWithBodyArgsType, ): Promise<ControllerResponse> { return handleRequest('PATCH', options); } /** * Generic DELETE request to Confluence API * * @param options - Options containing path, queryParams, and optional jq filter * @returns Promise with raw JSON response (optionally filtered) */ export async function handleDelete( options: GetApiToolArgsType, ): Promise<ControllerResponse> { return handleRequest('DELETE', options); }

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