UNPKG

@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
#!/usr/bin/env node "use strict"; 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); }); }