@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
175 lines (174 loc) • 7.01 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.startServer = startServer;
const mcp_js_1 = require("@modelcontextprotocol/sdk/server/mcp.js");
const streamableHttp_js_1 = require("@modelcontextprotocol/sdk/server/streamableHttp.js");
const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
const logger_util_js_1 = require("./utils/logger.util.js");
const config_util_js_1 = require("./utils/config.util.js");
const constants_util_js_1 = require("./utils/constants.util.js");
const index_js_1 = require("./cli/index.js");
const express_1 = __importDefault(require("express"));
const cors_1 = __importDefault(require("cors"));
// Import tools
const atlassian_api_tool_js_1 = __importDefault(require("./tools/atlassian.api.tool.js"));
const atlassian_repositories_tool_js_1 = __importDefault(require("./tools/atlassian.repositories.tool.js"));
// Create a contextualized logger for this file
const indexLogger = logger_util_js_1.Logger.forContext('index.ts');
// Log initialization at debug level
indexLogger.debug('Bitbucket MCP server module loaded');
let serverInstance = null;
let transportInstance = null;
/**
* Start the MCP server with the specified transport mode
*
* @param mode The transport mode to use (stdio or http)
* @returns Promise that resolves to the server instance when started successfully
*/
async function startServer(mode = 'stdio') {
const serverLogger = logger_util_js_1.Logger.forContext('index.ts', 'startServer');
// Load configuration
serverLogger.info('Starting MCP server initialization...');
config_util_js_1.config.load();
if (config_util_js_1.config.getBoolean('DEBUG')) {
serverLogger.debug('Debug mode enabled');
}
serverLogger.info(`Initializing Bitbucket MCP server v${constants_util_js_1.VERSION}`);
serverInstance = new mcp_js_1.McpServer({
name: constants_util_js_1.PACKAGE_NAME,
version: constants_util_js_1.VERSION,
});
// Register all tools
serverLogger.info('Registering MCP tools...');
atlassian_api_tool_js_1.default.registerTools(serverInstance);
atlassian_repositories_tool_js_1.default.registerTools(serverInstance);
serverLogger.info('All tools registered successfully');
if (mode === 'stdio') {
// STDIO Transport
serverLogger.info('Using STDIO transport for MCP communication');
transportInstance = new stdio_js_1.StdioServerTransport();
try {
await serverInstance.connect(transportInstance);
serverLogger.info('MCP server started successfully on STDIO transport');
setupGracefulShutdown();
return serverInstance;
}
catch (err) {
serverLogger.error('Failed to start server on STDIO transport', err);
process.exit(1);
}
}
else {
// HTTP Transport with Express
serverLogger.info('Using Streamable HTTP transport for MCP communication');
const app = (0, express_1.default)();
app.use((0, cors_1.default)());
app.use(express_1.default.json());
const mcpEndpoint = '/mcp';
serverLogger.debug(`MCP endpoint: ${mcpEndpoint}`);
// Create transport instance
const transport = new streamableHttp_js_1.StreamableHTTPServerTransport({
sessionIdGenerator: undefined,
});
// Connect server to transport
await serverInstance.connect(transport);
transportInstance = transport;
// Handle all MCP requests
app.all(mcpEndpoint, (req, res) => {
transport
.handleRequest(req, res, req.body)
.catch((err) => {
serverLogger.error('Error in transport.handleRequest', err);
if (!res.headersSent) {
res.status(500).json({
error: 'Internal Server Error',
});
}
});
});
// Health check endpoint
app.get('/', (_req, res) => {
res.send(`Bitbucket MCP Server v${constants_util_js_1.VERSION} is running`);
});
// Start HTTP server
const PORT = Number(process.env.PORT ?? 3000);
await new Promise((resolve) => {
app.listen(PORT, () => {
serverLogger.info(`HTTP transport listening on http://localhost:${PORT}${mcpEndpoint}`);
resolve();
});
});
setupGracefulShutdown();
return serverInstance;
}
}
/**
* Main entry point - this will run when executed directly
* Determines whether to run in CLI or server mode based on command-line arguments
*/
async function main() {
const mainLogger = logger_util_js_1.Logger.forContext('index.ts', 'main');
// Load configuration
config_util_js_1.config.load();
// CLI mode - if any arguments are provided
if (process.argv.length > 2) {
mainLogger.info('Starting in CLI mode');
await (0, index_js_1.runCli)(process.argv.slice(2));
mainLogger.info('CLI execution completed');
return;
}
// Server mode - determine transport
const transportMode = (process.env.TRANSPORT_MODE || 'stdio').toLowerCase();
let mode;
if (transportMode === 'stdio') {
mode = 'stdio';
}
else if (transportMode === 'http') {
mode = 'http';
}
else {
mainLogger.warn(`Unknown TRANSPORT_MODE "${transportMode}", defaulting to stdio`);
mode = 'stdio';
}
mainLogger.info(`Starting server with ${mode.toUpperCase()} transport`);
await startServer(mode);
mainLogger.info('Server is now running');
}
/**
* Set up graceful shutdown handlers for the server
*/
function setupGracefulShutdown() {
const shutdownLogger = logger_util_js_1.Logger.forContext('index.ts', 'shutdown');
const shutdown = async () => {
try {
shutdownLogger.info('Shutting down gracefully...');
if (transportInstance &&
'close' in transportInstance &&
typeof transportInstance.close === 'function') {
await transportInstance.close();
}
if (serverInstance && typeof serverInstance.close === 'function') {
await serverInstance.close();
}
process.exit(0);
}
catch (err) {
shutdownLogger.error('Error during shutdown', err);
process.exit(1);
}
};
['SIGINT', 'SIGTERM'].forEach((signal) => {
process.on(signal, shutdown);
});
}
// If this file is being executed directly (not imported), run the main function
if (require.main === module) {
main().catch((err) => {
indexLogger.error('Unhandled error in main process', err);
process.exit(1);
});
}