@aashari/mcp-server-atlassian-bitbucket
Version:
Node.js/TypeScript MCP server for Atlassian Bitbucket. Enables AI systems (LLMs) to interact with workspaces, repositories, and pull requests via tools (list, get, comment, search). Connects AI directly to version control workflows through the standard MC
139 lines (138 loc) • 5.2 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.handleGet = handleGet;
exports.handlePost = handlePost;
exports.handlePut = handlePut;
exports.handlePatch = handlePatch;
exports.handleDelete = handleDelete;
const transport_util_js_1 = require("../utils/transport.util.js");
const logger_util_js_1 = require("../utils/logger.util.js");
const error_handler_util_js_1 = require("../utils/error-handler.util.js");
const jq_util_js_1 = require("../utils/jq.util.js");
const error_util_js_1 = require("../utils/error.util.js");
// Logger instance for this module
const logger = logger_util_js_1.Logger.forContext('controllers/atlassian.api.controller.ts');
/**
* Normalizes the API path by ensuring it starts with /2.0
* @param path - The raw path provided by the user
* @returns Normalized path with /2.0 prefix
*/
function normalizePath(path) {
let normalizedPath = path;
if (!normalizedPath.startsWith('/')) {
normalizedPath = '/' + normalizedPath;
}
if (!normalizedPath.startsWith('/2.0')) {
normalizedPath = '/2.0' + 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;
}
/**
* 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, options) {
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 = (0, transport_util_js_1.getAtlassianCredentials)();
if (!credentials) {
throw (0, error_util_js_1.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,
};
// Add body for methods that support it
if (options.body && ['POST', 'PUT', 'PATCH'].includes(method)) {
fetchOptions.body = options.body;
}
const response = await (0, transport_util_js_1.fetchAtlassian)(credentials, path, fetchOptions);
methodLogger.debug('Successfully received response');
// Apply JQ filter if provided, otherwise return raw data
const result = (0, jq_util_js_1.applyJqFilter)(response.data, options.jq);
// Convert to output format (TOON by default, JSON if requested)
const useToon = options.outputFormat !== 'json';
const content = await (0, jq_util_js_1.toOutputString)(result, useToon);
return {
content,
rawResponsePath: response.rawResponsePath,
};
}
catch (error) {
throw (0, error_handler_util_js_1.handleControllerError)(error, {
entityType: 'API',
operation: `${method} request`,
source: `controllers/atlassian.api.controller.ts@handle${method}`,
additionalInfo: { path: options.path },
});
}
}
/**
* Generic GET request to Bitbucket API
*
* @param options - Options containing path, queryParams, and optional jq filter
* @returns Promise with raw JSON response (optionally filtered)
*/
async function handleGet(options) {
return handleRequest('GET', options);
}
/**
* Generic POST request to Bitbucket API
*
* @param options - Options containing path, body, queryParams, and optional jq filter
* @returns Promise with raw JSON response (optionally filtered)
*/
async function handlePost(options) {
return handleRequest('POST', options);
}
/**
* Generic PUT request to Bitbucket API
*
* @param options - Options containing path, body, queryParams, and optional jq filter
* @returns Promise with raw JSON response (optionally filtered)
*/
async function handlePut(options) {
return handleRequest('PUT', options);
}
/**
* Generic PATCH request to Bitbucket API
*
* @param options - Options containing path, body, queryParams, and optional jq filter
* @returns Promise with raw JSON response (optionally filtered)
*/
async function handlePatch(options) {
return handleRequest('PATCH', options);
}
/**
* Generic DELETE request to Bitbucket API
*
* @param options - Options containing path, queryParams, and optional jq filter
* @returns Promise with raw JSON response (optionally filtered)
*/
async function handleDelete(options) {
return handleRequest('DELETE', options);
}