C# + Bitbucket API + MCP
10K+
This repository contains Model Context Protocol (MCP) server implementations for Bitbucket Cloud integration.
MCP Tools are available for Bitbucket operations, including:
list_pull_open_requests: Gets all open pull requests in a Bitbucket repository, including draft status.list_pull_requests: Gets pull requests in a Bitbucket repository filtered by state (Open, Merged, Declined, Superseded), including draft status.get_pull_request_comments: Gets comments for a specific pull request.get_pull_request_details: Gets detailed information about a pull request including description, metadata (including draft status), and changed files.read_file: Reads the raw contents of a file in a repository at a given revision.list_directory: Shallow listing of files and directories in a repository path at a given revision.search_code: Searches code content across all repositories in the workspace.list_repositories: Lists repositories in the workspace, with optional name filtering.list_branches: Lists branches in a repository, with optional name filtering.list_commits: Lists commit history for a repository or branch.get_commit: Gets detailed information about a single commit by hash.From your Linux server, create a directory for your configuration:
mkdir -p /opt/bitbucket-mcp-server
cd /opt/bitbucket-mcp-server
Pull the Docker image:
docker pull peakflames/bitbucket-remote-mcp-server
Create an appsettings.json file tailored to your Bitbucket configuration:
{
"BitbucketCloudConfig": {
"AccountName": "your-workspace-name"
}
}
Run the container. See Authentication below to choose and configure a credential method — the example below uses the default shared-credential mode:
docker run -d \
--name bitbucket-mcp-server \
-p 8080:8080 \
-e BITBUCKET_MCP_USERNAME="your_bitbucket_username" \
-e BITBUCKET_MCP_API_TOKEN="your_bitbucket_app_password" \
-v $(pwd)/appsettings.json:/app/appsettings.json \
peakflames/bitbucket-remote-mcp-server
The server should now be running. MCP clients will connect using:
http://{{your-server-ip}}:8080/mcpappsettings.json)| Setting | Description | Required | Default |
|---|---|---|---|
AccountName | The Bitbucket workspace/account name | Yes | N/A |
Authentication credentials are retrieved from environment variables at startup — see Authentication below.
The server supports two ways to authenticate to Bitbucket, chosen by which environment variables and config sections are set:
The server supports two authentication methods: OAuth 2.0 Client Credentials and Basic Authentication. Configure one of these methods using environment variables.
OAuth 2.0 provides more secure authentication for server-to-server communication and is recommended for production deployments.
Linux/macOS:
export BITBUCKET_MCP_CONSUMER_KEY="your_consumer_key"
export BITBUCKET_MCP_SECRET_KEY="your_secret_key"
Windows (PowerShell):
$env:BITBUCKET_MCP_CONSUMER_KEY = "your_consumer_key"
$env:BITBUCKET_MCP_SECRET_KEY = "your_secret_key"
Windows (Command Prompt):
set BITBUCKET_MCP_CONSUMER_KEY=your_consumer_key
set BITBUCKET_MCP_SECRET_KEY=your_secret_key
How to obtain OAuth 2.0 credentials:
https://example.com/unusedOne consumer covers every environment. This flow (
client_credentials, RFC 6749 §4.4) sends the Consumer Key/Secret directly to Bitbucket's token endpoint and never sends aredirect_uri, so the consumer's registered Callback URL is never evaluated. The same key/secret pair works unchanged from a workstation, a shared dev server, and production — there is no need to mint a separate consumer per host. This is the opposite of the Broker's per-user OAuth, which does need one consumer per environment because it drives a real browser redirect back to each host's own callback URL.
Basic authentication uses your Bitbucket username and app password. This method is simpler to set up but less secure for production use.
Linux/macOS:
export BITBUCKET_MCP_USERNAME="your_username"
export BITBUCKET_MCP_API_TOKEN="your_app_password"
Windows (PowerShell):
$env:BITBUCKET_MCP_USERNAME = "your_username"
$env:BITBUCKET_MCP_API_TOKEN = "your_app_password"
Windows (Command Prompt):
set BITBUCKET_MCP_USERNAME=your_username
set BITBUCKET_MCP_API_TOKEN=your_app_password
How to create an app password:
The server automatically determines which authentication method to use based on the environment variables you set:
BITBUCKET_MCP_CONSUMER_KEY and BITBUCKET_MCP_SECRET_KEY are setBITBUCKET_MCP_USERNAME and BITBUCKET_MCP_API_TOKEN are setA shared credential (one of the two methods above) is required at startup, unless the Broker is enabled — see below.
appsettings.jsonProgram.cs, the application reads the authentication credentials from environment variablesread_file, list_pull_requests), a Bitbucket client is created using the shared credential and the repository slug passed as a tool argument. For workspace-scoped tools (list_repositories, search_code), the client is validated against the workspace itself instead — no repository name is neededInstead of one shared identity, the Broker turns the server into its own OAuth 2.1 authorization server that delegates sign-in to Bitbucket. Each caller connects, authenticates as themselves against Bitbucket, and every subsequent tool call resolves and uses that caller's own Bitbucket access token — so two different authenticated callers hitting the same tool see results scoped to their own real Bitbucket read permissions rather than one shared identity's.
The Broker is disabled by default and requires McpAuth:Enabled (the OAuth 2.1 resource-server
gate in front of /mcp) to also be enabled — without it, no caller would present a token for the
Broker to resolve, and the server refuses to start rather than accept tool calls it can't
authenticate.
"McpAuth": {
"Enabled": true,
"Issuer": "", // ignored once Broker:Enabled is true — see below
"ResourceUri": "", // this server's own canonical URL, e.g. https://your-mcp-server-url/mcp
"ScopesSupported": ["bitbucket:read"],
"ClockSkewSeconds": 30
},
"Broker": {
"Enabled": true,
"DatabasePath": "data/broker.db", // relative paths resolve against the working directory
"IssuerUri": "", // this server's own base URL, e.g. https://your-mcp-server-url
"UpstreamAuthorizeUrl": "https://bitbucket.org/site/oauth2/authorize",
"UpstreamTokenUrl": "https://bitbucket.org/site/oauth2/access_token",
"UpstreamUserInfoUrl": "https://api.bitbucket.org/2.0/user",
"UpstreamClientId": "", // the Bitbucket OAuth consumer's key
"UpstreamClientSecret": "", // the Bitbucket OAuth consumer's secret — see below, do not put this inline
"UpstreamScopes": ["account", "repository", "pullrequest"],
"DcrEnabled": false, // POST /register (RFC 7591) — built, off by default
"StaticClients": [], // pre-registered public clients: [{ "ClientId": "...", "RedirectUris": ["..."] }]
"TransactionLifetimeMinutes": 15,
"ClientCodeLifetimeMinutes": 5,
"IssuedAccessTokenLifetimeMinutes": 60,
"IssuedRefreshTokenLifetimeDays": 30
}
With Broker:Enabled, McpAuth's resource-server gate automatically trusts this server's own
signing key (persisted in the database, so a restart doesn't invalidate outstanding tokens)
instead of fetching discovery/JWKS from McpAuth:Issuer — that setting still has to be a
syntactically valid URI to pass validation, but its value stops mattering once the Broker is on.
Supplying Broker:UpstreamClientSecret (and any other Broker secret). Do not put a real
secret value inline in appsettings.json — per this repo's convention, every secret is supplied
through an environment variable at boot, using the ASP.NET Core double-underscore (__)
convention to address a nested config key:
export Broker__UpstreamClientSecret="your_bitbucket_oauth_consumer_secret"
$env:Broker__UpstreamClientSecret = "your_bitbucket_oauth_consumer_secret"
Mounting the SQLite database. Broker:DatabasePath defaults to data/broker.db, relative to
the working directory — /app/data/broker.db inside the container. Mount a persistent volume
there, or every user's stored token is lost on container restart (the server falls back to a
temp-directory path and logs a warning if the configured directory isn't writable — that fallback
does not survive a restart):
docker run -d \
--name bitbucket-mcp-server \
-p 8080:8080 \
-e McpAuth__Enabled="true" \
-e McpAuth__ResourceUri="https://your-mcp-server-url/mcp" \
-e Broker__Enabled="true" \
-e Broker__IssuerUri="https://your-mcp-server-url" \
-e Broker__UpstreamClientId="your_bitbucket_oauth_consumer_key" \
-e Broker__UpstreamClientSecret="your_bitbucket_oauth_consumer_secret" \
-v $(pwd)/appsettings.json:/app/appsettings.json \
-v $(pwd)/data:/app/data \
peakflames/bitbucket-remote-mcp-server
Requesting the Bitbucket OAuth consumer: unlike the shared client-credentials method above, the Broker drives a real browser redirect back to Bitbucket, so its consumer registration matters and is not interchangeable across hosts.
Consumer type — check This is a private consumer in the Bitbucket UI.
Callback URL — <Broker:IssuerUri>/oauth/callback, exactly matching the value the Broker sends
as redirect_uri.
Permissions — scoped to read-only Account/Repositories/Pull requests.
One consumer per environment. Bitbucket matches the incoming redirect_uri against the
consumer's configured Callback URL with scheme and host (including port) matched exactly, and
only the path matched by prefix. A mismatch on scheme or host is rejected outright — it is not a
looser "prefix" match on the whole URL. Concretely:
| Environment | Broker:IssuerUri | Consumer Callback URL |
|---|---|---|
| Local dev | http://localhost:5107 | http://localhost:5107/oauth/callback |
| Shared dev server | https://mcp-dev.example.com | https://mcp-dev.example.com/oauth/callback |
| Production | https://mcp.example.com | https://mcp.example.com/oauth/callback |
A localhost consumer cannot also serve a production host, and vice versa — register a separate
consumer for each. If the scheme or host doesn't match, Bitbucket's token endpoint rejects the
exchange with "Scheme must match configured redirect uri" or "host must match configured redirect uri"; seeing either error means the wrong consumer (or the wrong Broker:IssuerUri) is
configured for this environment.
Broker:IssuerUri and McpAuth:ResourceUri constraints — the server checks these with
ValidateOnStart, so a value that breaks them fails at boot with a clear error:
Broker:IssuerUri must be an absolute URI, https outside the Development environment
(plain http is only accepted in Development), with no fragment.McpAuth:ResourceUri must be an absolute URI with no fragment. Its scheme is not checked.Two further rules are not checked at startup; a value that breaks them only fails later, at sign-in:
Broker:IssuerUri must have no path and no trailing slash. The Broker serves its endpoints at the
host root and builds every URL it advertises by appending to this value ({IssuerUri}/authorize,
{IssuerUri}/oauth/callback, and so on), so a trailing slash yields //oauth/callback, which no
longer matches the Bitbucket consumer's Callback URL.McpAuth:ResourceUri is this server's full public MCP URL, including the /mcp path (e.g.
https://your-mcp-server-url/mcp). It is the resource advertised in the protected-resource
metadata and the audience of every token the Broker issues, so set it to exactly the URL clients
connect to.Broker:StaticClients is the non-DCR way to pre-register a client (e.g. Claude Code with a
fixed oauth.clientId) — public clients only, no secret; PKCE is the confidentiality mechanism.
DCR-registered clients (Broker:DcrEnabled: true) live in the database instead.
The database needs exactly one writer, so run at most one replica when the Broker is enabled. Bitbucket access/refresh tokens are stored in plaintext (they must be replayed to Bitbucket verbatim); everything else that only needs to be verified — client codes, the refresh tokens this server issues, DCR client secrets — is stored hashed. Encryption of the database file itself is a volume-level concern, not something this application layer does.
Open Cline's MCP settings UI
Click the "Remote Servers" tab
Add the following configuration:
{
"mcpServers": {
"Bitbucket": {
"autoApprove": [],
"disabled": false,
"timeout": 60,
"url": "http://{{your-server-ip}}:8080/mcp",
"transportType": "streamableHttp"
}
}
}
"Environment variable not set" error:
BITBUCKET_MCP_CONSUMER_KEY and BITBUCKET_MCP_SECRET_KEY are setBITBUCKET_MCP_USERNAME and BITBUCKET_MCP_API_TOKEN are setBroker:Enabled is true — in that case no shared credential is neededConnection errors:
Permission errors:
For development setup, building instructions, Docker container publishing, and debugging information, see CONTRIBUTING.md.
See LICENSE for details.
Content type
Image
Digest
sha256:a775c26d0…
Size
20.2 MB
Last updated
11 days ago
docker pull peakflames/bitbucket-remote-mcp-server