UNPKG

@zhanglc77/bitbucket-mcp-server

Version:

MCP server for Bitbucket API integration - supports both Cloud and Server

149 lines 7.56 kB
#!/usr/bin/env node import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { CallToolRequestSchema, ErrorCode, ListResourcesRequestSchema, ReadResourceRequestSchema, ListToolsRequestSchema, McpError, } from '@modelcontextprotocol/sdk/types.js'; // TODO: Read version from package.json when not in test environment const version = '1.0.12'; import { BitbucketApiClient } from './utils/api-client.js'; import { PullRequestHandlers } from './handlers/pull-request-handlers.js'; import { BranchHandlers } from './handlers/branch-handlers.js'; import { ReviewHandlers } from './handlers/review-handlers.js'; import { FileHandlers } from './handlers/file-handlers.js'; import { SearchHandlers } from './handlers/search-handlers.js'; import { ResourceHandlers } from './handlers/resource-handlers.js'; import { toolDefinitions } from './tools/definitions.js'; // Get environment variables const BITBUCKET_USERNAME = process.env.BITBUCKET_USERNAME; const BITBUCKET_APP_PASSWORD = process.env.BITBUCKET_APP_PASSWORD; const BITBUCKET_TOKEN = process.env.BITBUCKET_TOKEN; // For Bitbucket Server const BITBUCKET_BASE_URL = process.env.BITBUCKET_BASE_URL || 'https://api.bitbucket.org/2.0'; // Check for either app password (Cloud) or token (Server) if (!BITBUCKET_USERNAME || (!BITBUCKET_APP_PASSWORD && !BITBUCKET_TOKEN)) { console.error('Error: BITBUCKET_USERNAME and either BITBUCKET_APP_PASSWORD (for Cloud) or BITBUCKET_TOKEN (for Server) are required'); console.error('Please set these in your MCP settings configuration'); process.exit(1); } export class BitbucketMCPServer { server; apiClient; pullRequestHandlers; branchHandlers; reviewHandlers; fileHandlers; searchHandlers; resourceHandlers; constructor() { this.server = new Server({ name: 'bitbucket-mcp-server', version: version, }, { capabilities: { tools: {}, resources: {}, }, }); // Initialize API client this.apiClient = new BitbucketApiClient(BITBUCKET_BASE_URL, BITBUCKET_USERNAME, BITBUCKET_APP_PASSWORD, BITBUCKET_TOKEN); // Initialize handlers this.pullRequestHandlers = new PullRequestHandlers(this.apiClient, BITBUCKET_BASE_URL, BITBUCKET_USERNAME); this.branchHandlers = new BranchHandlers(this.apiClient, BITBUCKET_BASE_URL); this.reviewHandlers = new ReviewHandlers(this.apiClient, BITBUCKET_USERNAME); this.fileHandlers = new FileHandlers(this.apiClient, BITBUCKET_BASE_URL); this.searchHandlers = new SearchHandlers(this.apiClient, BITBUCKET_BASE_URL); this.resourceHandlers = new ResourceHandlers(); this.setupToolHandlers(); this.setupResourceHandlers(); // Error handling this.server.onerror = (error) => console.error('[MCP Error]', error); process.on('SIGINT', async () => { await this.server.close(); process.exit(0); }); } setupToolHandlers() { // List available tools this.server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: toolDefinitions, })); // Handle tool calls this.server.setRequestHandler(CallToolRequestSchema, async (request) => { switch (request.params.name) { // Pull Request tools case 'get_pull_request': return this.pullRequestHandlers.handleGetPullRequest(request.params.arguments); case 'list_pull_requests': return this.pullRequestHandlers.handleListPullRequests(request.params.arguments); case 'create_pull_request': return this.pullRequestHandlers.handleCreatePullRequest(request.params.arguments); case 'update_pull_request': return this.pullRequestHandlers.handleUpdatePullRequest(request.params.arguments); case 'add_comment': return this.pullRequestHandlers.handleAddComment(request.params.arguments); case 'merge_pull_request': return this.pullRequestHandlers.handleMergePullRequest(request.params.arguments); case 'list_pr_commits': return this.pullRequestHandlers.handleListPrCommits(request.params.arguments); // Branch tools case 'list_branches': return this.branchHandlers.handleListBranches(request.params.arguments); case 'delete_branch': return this.branchHandlers.handleDeleteBranch(request.params.arguments); case 'get_branch': return this.branchHandlers.handleGetBranch(request.params.arguments); case 'list_branch_commits': return this.branchHandlers.handleListBranchCommits(request.params.arguments); // Code Review tools case 'get_pull_request_diff': return this.reviewHandlers.handleGetPullRequestDiff(request.params.arguments); case 'approve_pull_request': return this.reviewHandlers.handleApprovePullRequest(request.params.arguments); case 'unapprove_pull_request': return this.reviewHandlers.handleUnapprovePullRequest(request.params.arguments); case 'request_changes': return this.reviewHandlers.handleRequestChanges(request.params.arguments); case 'remove_requested_changes': return this.reviewHandlers.handleRemoveRequestedChanges(request.params.arguments); // File tools case 'list_directory_content': return this.fileHandlers.handleListDirectoryContent(request.params.arguments); case 'get_file_content': return this.fileHandlers.handleGetFileContent(request.params.arguments); // Search tools case 'search_code': return this.searchHandlers.handleSearchCode(request.params.arguments); default: throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${request.params.name}`); } }); } setupResourceHandlers() { // List available resources and templates this.server.setRequestHandler(ListResourcesRequestSchema, async () => this.resourceHandlers.listResources()); // Read resource content this.server.setRequestHandler(ReadResourceRequestSchema, async (request) => this.resourceHandlers.readResource(request.params.uri)); } // Public methods for testing getCapabilities() { return { tools: {}, resources: {} }; } async listTools() { return { tools: toolDefinitions }; } async listResources() { return this.resourceHandlers.listResources(); } async readResource(uri) { return this.resourceHandlers.readResource(uri); } async run() { const transport = new StdioServerTransport(); await this.server.connect(transport); console.error(`Bitbucket MCP server running on stdio (${this.apiClient.getIsServer() ? 'Server' : 'Cloud'} mode)`); } } const server = new BitbucketMCPServer(); server.run().catch(console.error); //# sourceMappingURL=index.js.map