Initial commit: Bitbucket MCP server with get_pull_request tool
This commit is contained in:
commit
b1d758646c
9 changed files with 1909 additions and 0 deletions
42
.gitignore
vendored
Normal file
42
.gitignore
vendored
Normal 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
134
README.md
Normal 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
64
SETUP_GUIDE.md
Normal 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
61
SETUP_GUIDE_SERVER.md
Normal 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
1105
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
23
package.json
Normal file
23
package.json
Normal 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
48
scripts/setup-auth.js
Normal 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
413
src/index.ts
Normal 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
19
tsconfig.json
Normal 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"]
|
||||
}
|
Loading…
Reference in a new issue