Initial commit: Bitbucket MCP server with get_pull_request tool

This commit is contained in:
pdogra1299 2025-06-04 17:24:37 +05:30
commit b1d758646c
9 changed files with 1909 additions and 0 deletions

42
.gitignore vendored Normal file
View file

@ -0,0 +1,42 @@
# Dependencies
node_modules/
# Build output
build/
dist/
# Environment files
.env
.env.local
.env.*.local
# IDE files
.vscode/
.idea/
*.swp
*.swo
*~
# OS files
.DS_Store
Thumbs.db
# Logs
logs/
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Test files
test-api.js
*.test.js
# Temporary files
*.tmp
*.temp
.cache/
# Personal configuration
RELOAD_INSTRUCTIONS.md
personal-notes.md

134
README.md Normal file
View file

@ -0,0 +1,134 @@
# Bitbucket MCP Server
An MCP (Model Context Protocol) server that provides tools for interacting with the Bitbucket API.
## Features
Currently implemented:
- `get_pull_request` - Retrieve detailed information about a pull request
Planned features:
- `create_pull_request` - Create new pull requests
- `list_pull_requests` - List pull requests with filters
- `update_pull_request` - Update PR details
- `merge_pull_request` - Merge pull requests
- And more...
## Installation
1. Clone or download this repository
2. Install dependencies:
```bash
npm install
```
3. Build the TypeScript code:
```bash
npm run build
```
## Authentication Setup
This server uses Bitbucket App Passwords for authentication.
### Creating an App Password
1. Log in to your Bitbucket account
2. Navigate to: https://bitbucket.org/account/settings/app-passwords/
3. Click "Create app password"
4. Give it a descriptive label (e.g., "MCP Server")
5. Select the following permissions:
- **Account**: Read
- **Repositories**: Read, Write
- **Pull requests**: Read, Write
6. Click "Create"
7. **Important**: Copy the generated password immediately (you won't be able to see it again!)
### Running the Setup Script
```bash
node scripts/setup-auth.js
```
This will guide you through the authentication setup process.
## Configuration
Add the server to your MCP settings file (usually located at `~/.vscode-server/data/User/globalStorage/saoudrizwan.claude-dev/settings/cline_mcp_settings.json`):
```json
{
"mcpServers": {
"bitbucket": {
"command": "node",
"args": ["/absolute/path/to/bitbucket-mcp-server/build/index.js"],
"env": {
"BITBUCKET_USERNAME": "your-username",
"BITBUCKET_APP_PASSWORD": "your-app-password"
}
}
}
}
```
Replace:
- `/absolute/path/to/bitbucket-mcp-server` with the actual path to this directory
- `your-username` with your Bitbucket username (not email)
- `your-app-password` with the app password you created
For Bitbucket Server, use:
```json
{
"mcpServers": {
"bitbucket": {
"command": "node",
"args": ["/absolute/path/to/bitbucket-mcp-server/build/index.js"],
"env": {
"BITBUCKET_USERNAME": "your-username",
"BITBUCKET_TOKEN": "your-http-access-token",
"BITBUCKET_BASE_URL": "https://bitbucket.yourcompany.com"
}
}
}
}
```
## Usage
Once configured, you can use the available tools:
### Get Pull Request
```typescript
{
"tool": "get_pull_request",
"arguments": {
"workspace": "PROJ", // Required - your project key
"repository": "my-repo",
"pull_request_id": 123
}
}
```
Returns detailed information about the pull request including:
- Title and description
- Author and reviewers
- Source and destination branches
- Approval status
- Links to web UI and diff
- And more...
## Development
- `npm run dev` - Watch mode for development
- `npm run build` - Build the TypeScript code
- `npm start` - Run the built server
## Troubleshooting
1. **Authentication errors**: Double-check your username and app password
2. **404 errors**: Verify the workspace, repository slug, and PR ID
3. **Permission errors**: Ensure your app password has the required permissions
## License
MIT

64
SETUP_GUIDE.md Normal file
View file

@ -0,0 +1,64 @@
# Bitbucket MCP Server Setup Guide
## Step 1: Find Your Bitbucket Username
1. **Log in to Bitbucket**: Go to https://bitbucket.org and log in with your credentials
2. **Find your username**:
- After logging in, click on your profile avatar in the top-right corner
- Click on "Personal settings" or go directly to: https://bitbucket.org/account/settings/
- Your username will be displayed at the top of the page
- **Note**: Your username is NOT your email address. It's usually a shorter identifier like "johndoe" or "jdoe123"
## Step 2: Create an App Password
1. **Navigate to App Passwords**:
- While logged in, go to: https://bitbucket.org/account/settings/app-passwords/
- Or from your account settings, look for "App passwords" in the left sidebar under "Access management"
2. **Create a new app password**:
- Click the "Create app password" button
- Give it a descriptive label like "MCP Server" or "Bitbucket MCP Integration"
3. **Select permissions** (IMPORTANT - select these specific permissions):
- ✅ **Account**: Read
- ✅ **Repositories**: Read, Write
- ✅ **Pull requests**: Read, Write
- You can leave other permissions unchecked
4. **Generate the password**:
- Click "Create"
- **IMPORTANT**: Copy the generated password immediately! It will look something like: `ATBBxxxxxxxxxxxxxxxxxxxxx`
- You won't be able to see this password again after closing the dialog
## Step 3: Find Your Workspace (Optional but Recommended)
Your workspace is the organization or team name in Bitbucket. To find it:
1. Look at any of your repository URLs:
- Example: `https://bitbucket.org/mycompany/my-repo`
- In this case, "mycompany" is your workspace
2. Or go to your workspace dashboard:
- Click on "Workspaces" in the top navigation
- Your workspaces will be listed there
## Example Credentials
Here's what your credentials should look like:
```
Username: johndoe # Your Bitbucket username (NOT email)
App Password: ATBB3xXx... # The generated app password
Workspace: mycompany # Your organization/workspace name
```
## Common Issues
1. **"Username not found"**: Make sure you're using your Bitbucket username, not your email address
2. **"Invalid app password"**: Ensure you copied the entire app password including the "ATBB" prefix
3. **"Permission denied"**: Check that your app password has the required permissions (Account: Read, Repositories: Read/Write, Pull requests: Read/Write)
## Next Steps
Once you have these credentials, share them with me and I'll configure the MCP server for you. The credentials will be stored securely in your MCP settings configuration.

61
SETUP_GUIDE_SERVER.md Normal file
View file

@ -0,0 +1,61 @@
# Bitbucket Server MCP Setup Guide
Since you're using Bitbucket Server (self-hosted), you'll need to create an HTTP access token instead of an app password.
## Step 1: Your Username
Your Bitbucket Server username (not email address)
## Step 2: Create an HTTP Access Token
1. **Navigate to HTTP Access Tokens**:
- You mentioned you can see "HTTP access tokens" in your account settings
- Click on that option
2. **Create a new token**:
- Click "Create token" or similar button
- Give it a descriptive name like "MCP Server Integration"
- Set an expiration date (or leave it without expiration if allowed)
- Select the following permissions:
- **Repository**: Read, Write
- **Pull request**: Read, Write
- **Project**: Read (if available)
3. **Generate and copy the token**:
- Click "Create" or "Generate"
- **IMPORTANT**: Copy the token immediately! It will look like a long string of random characters
- You won't be able to see this token again
## Step 3: Find Your Bitbucket Server URL
Your Bitbucket Server URL is the base URL you use to access Bitbucket. For example:
- `https://bitbucket.yourcompany.com`
- `https://git.yourcompany.com`
- `https://bitbucket.internal.company.net`
## Step 4: Find Your Project/Workspace
In Bitbucket Server, repositories are organized by projects. Look at any repository URL:
- Example: `https://bitbucket.company.com/projects/PROJ/repos/my-repo`
- In this case, "PROJ" is your project key
## Example Configuration
For Bitbucket Server, your configuration will look like:
```
Username: your.username
Token: [Your HTTP access token]
Base URL: https://bitbucket.yourcompany.com
Project/Workspace: PROJ (or whatever your project key is)
```
## Next Steps
Once you have:
1. Your username
2. An HTTP access token from the "HTTP access tokens" section
3. Your Bitbucket Server base URL
4. Your project key
You can configure the MCP server for Bitbucket Server.

1105
package-lock.json generated Normal file

File diff suppressed because it is too large Load diff

23
package.json Normal file
View file

@ -0,0 +1,23 @@
{
"name": "bitbucket-mcp-server",
"version": "0.1.0",
"description": "MCP server for Bitbucket API integration",
"type": "module",
"main": "./build/index.js",
"scripts": {
"build": "tsc && node -e \"require('fs').chmodSync('build/index.js', '755')\"",
"dev": "tsc --watch",
"start": "node build/index.js"
},
"keywords": ["mcp", "bitbucket", "api"],
"author": "",
"license": "MIT",
"dependencies": {
"@modelcontextprotocol/sdk": "^1.12.1",
"axios": "^1.9.0"
},
"devDependencies": {
"@types/node": "^22.15.29",
"typescript": "^5.8.3"
}
}

48
scripts/setup-auth.js Normal file
View file

@ -0,0 +1,48 @@
#!/usr/bin/env node
console.log(`
===========================================
Bitbucket MCP Server - Authentication Setup
===========================================
To use this MCP server, you need to create a Bitbucket App Password.
Follow these steps:
1. Log in to your Bitbucket account
2. Go to: https://bitbucket.org/account/settings/app-passwords/
3. Click "Create app password"
4. Give it a label (e.g., "MCP Server")
5. Select the following permissions:
- Account: Read
- Repositories: Read, Write
- Pull requests: Read, Write
6. Click "Create"
7. Copy the generated app password (you won't be able to see it again!)
You'll need to provide:
- Your Bitbucket username (not email)
- The app password you just created
- Your default workspace/organization (optional)
Example workspace: If your repository URL is:
https://bitbucket.org/mycompany/my-repo
Then your workspace is: mycompany
These will be added to your MCP settings configuration.
Press Enter to continue...
`);
// Wait for user to press Enter
process.stdin.once('data', () => {
console.log(`
Next steps:
1. The MCP server will be configured with your credentials
2. You'll be able to use the 'get_pull_request' tool
3. More tools can be added later (create_pull_request, list_pull_requests, etc.)
Configuration complete!
`);
process.exit(0);
});

413
src/index.ts Normal file
View file

@ -0,0 +1,413 @@
#!/usr/bin/env node
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import {
CallToolRequestSchema,
ErrorCode,
ListToolsRequestSchema,
McpError,
} from '@modelcontextprotocol/sdk/types.js';
import axios, { AxiosInstance } from 'axios';
// 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_WORKSPACE = process.env.BITBUCKET_WORKSPACE;
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);
}
// Note: BITBUCKET_WORKSPACE is optional - it should be passed when invoking the tool
// Bitbucket Server API response types
interface BitbucketServerPullRequest {
id: number;
version: number;
title: string;
description?: string;
state: string;
open: boolean;
closed: boolean;
createdDate: number;
updatedDate: number;
fromRef: {
id: string;
displayId: string;
latestCommit: string;
repository: {
slug: string;
name: string;
project: {
key: string;
};
};
};
toRef: {
id: string;
displayId: string;
latestCommit: string;
repository: {
slug: string;
name: string;
project: {
key: string;
};
};
};
locked: boolean;
author: {
user: {
name: string;
emailAddress: string;
displayName: string;
};
role: string;
approved: boolean;
status: string;
};
reviewers: Array<{
user: {
name: string;
emailAddress: string;
displayName: string;
};
role: string;
approved: boolean;
status: string;
}>;
participants: Array<{
user: {
name: string;
emailAddress: string;
displayName: string;
};
role: string;
approved: boolean;
status: string;
}>;
links: {
self: Array<{
href: string;
}>;
};
}
// Bitbucket Cloud API response types (keeping for compatibility)
interface BitbucketCloudPullRequest {
id: number;
title: string;
description: string;
state: string;
author: {
display_name: string;
account_id: string;
};
source: {
branch: {
name: string;
};
repository: {
full_name: string;
};
};
destination: {
branch: {
name: string;
};
repository: {
full_name: string;
};
};
reviewers: Array<{
display_name: string;
account_id: string;
}>;
participants: Array<{
user: {
display_name: string;
account_id: string;
};
role: string;
approved: boolean;
}>;
created_on: string;
updated_on: string;
links: {
html: {
href: string;
};
self: {
href: string;
};
diff: {
href: string;
};
};
merge_commit?: {
hash: string;
};
close_source_branch: boolean;
closed_by?: {
display_name: string;
account_id: string;
};
}
// Type guard for tool arguments
const isGetPullRequestArgs = (
args: any
): args is { workspace: string; repository: string; pull_request_id: number } =>
typeof args === 'object' &&
args !== null &&
typeof args.workspace === 'string' &&
typeof args.repository === 'string' &&
typeof args.pull_request_id === 'number';
class BitbucketMCPServer {
private server: Server;
private axiosInstance: AxiosInstance;
private isServer: boolean;
constructor() {
this.isServer = !!BITBUCKET_TOKEN;
this.server = new Server(
{
name: 'bitbucket-mcp-server',
version: '0.1.0',
},
{
capabilities: {
tools: {},
},
}
);
// Create axios instance with appropriate auth
const axiosConfig: any = {
baseURL: BITBUCKET_BASE_URL,
headers: {
'Content-Type': 'application/json',
},
};
// Use token auth for Bitbucket Server, basic auth for Cloud
if (BITBUCKET_TOKEN) {
// Bitbucket Server uses Bearer token
axiosConfig.headers['Authorization'] = `Bearer ${BITBUCKET_TOKEN}`;
} else {
// Bitbucket Cloud uses basic auth with app password
axiosConfig.auth = {
username: BITBUCKET_USERNAME!,
password: BITBUCKET_APP_PASSWORD!,
};
}
this.axiosInstance = axios.create(axiosConfig);
this.setupToolHandlers();
// Error handling
this.server.onerror = (error) => console.error('[MCP Error]', error);
process.on('SIGINT', async () => {
await this.server.close();
process.exit(0);
});
}
private formatServerResponse(pr: BitbucketServerPullRequest): any {
const webUrl = `${BITBUCKET_BASE_URL}/projects/${pr.toRef.repository.project.key}/repos/${pr.toRef.repository.slug}/pull-requests/${pr.id}`;
return {
id: pr.id,
title: pr.title,
description: pr.description || 'No description provided',
state: pr.state,
is_open: pr.open,
is_closed: pr.closed,
author: pr.author.user.displayName,
author_email: pr.author.user.emailAddress,
source_branch: pr.fromRef.displayId,
destination_branch: pr.toRef.displayId,
source_commit: pr.fromRef.latestCommit,
destination_commit: pr.toRef.latestCommit,
reviewers: pr.reviewers.map(r => ({
name: r.user.displayName,
approved: r.approved,
status: r.status,
})),
participants: pr.participants.map(p => ({
name: p.user.displayName,
role: p.role,
approved: p.approved,
status: p.status,
})),
created_on: new Date(pr.createdDate).toLocaleString(),
updated_on: new Date(pr.updatedDate).toLocaleString(),
web_url: webUrl,
api_url: pr.links.self[0]?.href || '',
is_locked: pr.locked,
};
}
private formatCloudResponse(pr: BitbucketCloudPullRequest): any {
return {
id: pr.id,
title: pr.title,
description: pr.description || 'No description provided',
state: pr.state,
author: pr.author.display_name,
source_branch: pr.source.branch.name,
destination_branch: pr.destination.branch.name,
reviewers: pr.reviewers.map(r => r.display_name),
participants: pr.participants.map(p => ({
name: p.user.display_name,
role: p.role,
approved: p.approved,
})),
created_on: new Date(pr.created_on).toLocaleString(),
updated_on: new Date(pr.updated_on).toLocaleString(),
web_url: pr.links.html.href,
api_url: pr.links.self.href,
diff_url: pr.links.diff.href,
is_merged: pr.state === 'MERGED',
merge_commit: pr.merge_commit?.hash,
close_source_branch: pr.close_source_branch,
closed_by: pr.closed_by?.display_name,
};
}
private setupToolHandlers() {
// List available tools
this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
tools: [
{
name: 'get_pull_request',
description: 'Get details of a Bitbucket pull request',
inputSchema: {
type: 'object',
properties: {
workspace: {
type: 'string',
description: 'Bitbucket workspace/project key (e.g., "JBIZ")',
},
repository: {
type: 'string',
description: 'Repository slug (e.g., "my-repo")',
},
pull_request_id: {
type: 'number',
description: 'Pull request ID',
},
},
required: ['workspace', 'repository', 'pull_request_id'],
},
},
],
}));
// Handle tool calls
this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
if (request.params.name !== 'get_pull_request') {
throw new McpError(
ErrorCode.MethodNotFound,
`Unknown tool: ${request.params.name}`
);
}
if (!isGetPullRequestArgs(request.params.arguments)) {
throw new McpError(
ErrorCode.InvalidParams,
'Invalid arguments for get_pull_request'
);
}
const { workspace, repository, pull_request_id } = request.params.arguments;
try {
// Different API paths for Server vs Cloud
const apiPath = this.isServer
? `/rest/api/1.0/projects/${workspace}/repos/${repository}/pull-requests/${pull_request_id}` // Server
: `/repositories/${workspace}/${repository}/pullrequests/${pull_request_id}`; // Cloud
console.error(`[DEBUG] Fetching PR from: ${BITBUCKET_BASE_URL}${apiPath}`);
const response = await this.axiosInstance.get(apiPath);
const pr = response.data;
// Format the response based on server type
const formattedResponse = this.isServer
? this.formatServerResponse(pr as BitbucketServerPullRequest)
: this.formatCloudResponse(pr as BitbucketCloudPullRequest);
return {
content: [
{
type: 'text',
text: JSON.stringify(formattedResponse, null, 2),
},
],
};
} catch (error) {
if (axios.isAxiosError(error)) {
const status = error.response?.status;
const message = error.response?.data?.errors?.[0]?.message ||
error.response?.data?.error?.message ||
error.response?.data?.message ||
error.message;
console.error(`[DEBUG] API Error: ${status} - ${message}`);
console.error(`[DEBUG] Full error response:`, error.response?.data);
if (status === 404) {
return {
content: [
{
type: 'text',
text: `Pull request not found: ${workspace}/${repository}/pull-requests/${pull_request_id}`,
},
],
isError: true,
};
} else if (status === 401) {
return {
content: [
{
type: 'text',
text: `Authentication failed. Please check your ${this.isServer ? 'BITBUCKET_TOKEN' : 'BITBUCKET_USERNAME and BITBUCKET_APP_PASSWORD'}`,
},
],
isError: true,
};
}
return {
content: [
{
type: 'text',
text: `Bitbucket API error: ${message}`,
},
],
isError: true,
};
}
throw error;
}
});
}
async run() {
const transport = new StdioServerTransport();
await this.server.connect(transport);
console.error(`Bitbucket MCP server running on stdio (${this.isServer ? 'Server' : 'Cloud'} mode)`);
}
}
const server = new BitbucketMCPServer();
server.run().catch(console.error);

19
tsconfig.json Normal file
View file

@ -0,0 +1,19 @@
{
"compilerOptions": {
"target": "ES2022",
"module": "Node16",
"moduleResolution": "Node16",
"outDir": "./build",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"declaration": true,
"declarationMap": true,
"sourceMap": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "build"]
}