MCP Bitbucket
MCP Bitbucket š¦
An MCP server for Bitbucket Cloud ā pull request review, repository and source tools, and a searchable index of all 294 REST endpoints so the model can reach anything the typed tools do not wrap.
Derived from and substantially rewritten out of Kallows/mcp-bitbucket ā see Credits & Acknowledgments.
Features
Complete API coverage. Two tools ā
bb_list_endpointsandbb_requestā reach every Bitbucket Cloud 2.0 endpoint, so a missing typed tool is never a dead end. The endpoint index is generated from Atlassian's official OpenAPI spec and cannot drift from the published docs.Pull request review tools built for reviewing rather than for API completeness: per-file diffs, trimmed PR listings, inline comments, approvals.
Zero-config repo targeting. The workspace and repo slug are read from the git remote of the directory the client was launched in, so one registration serves every Bitbucket checkout.
Two auth modes. Scoped API tokens (preferred) or legacy app passwords.
Safe by default. Writes through the generic proxy are opt-in, repository deletion is not registered unless explicitly enabled, request logging is off, and every tool is annotated so your client can tell a read from a deletion.
Installation
Requires Python 3.12+.
git clone https://github.com/mohitgoel188/mcp-bitbucket.git
cd mcp-bitbucket
# with uv (recommended)
uv sync
# or with pip
pip install -e .Configuration
Register the server with your MCP client, pointing at the interpreter from the virtualenv you just created.
Do not launch this server with uv run --directory. That flag changes the
process working directory to this repo, which breaks
repository auto-detection ā every call would
resolve to mcp-bitbucket instead of the repo you are working in. Invoke the
interpreter directly, as shown below.
Claude Code
claude mcp add bitbucket \
--env BITBUCKET_TOKEN=your-api-token \
-- /absolute/path/to/mcp-bitbucket/.venv/bin/python -m mcp_bitbucket.serverClaude Desktop
~/Library/Application Support/Claude/claude_desktop_config.json on macOS,
%APPDATA%\Claude\claude_desktop_config.json on Windows:
{
"mcpServers": {
"bitbucket": {
"command": "/absolute/path/to/mcp-bitbucket/.venv/bin/python",
"args": ["-m", "mcp_bitbucket.server"],
"env": {
"BITBUCKET_TOKEN": "your-api-token"
}
}
}
}On Windows the command is
C:\\path\\to\\mcp-bitbucket\\.venv\\Scripts\\python.exe ā note that JSON
requires each backslash to be doubled.
Environment Variables
Copy .env.example as a starting point.
Credentials ā one of these two is required
# Preferred: a scoped API token or repository access token. No username needed.
# A token wins when both modes are configured, because Atlassian is migrating
# Bitbucket Cloud off app passwords onto scoped tokens.
export BITBUCKET_TOKEN="your-api-token"
# Legacy: username + app password.
export BITBUCKET_USERNAME="your-username"
export BITBUCKET_APP_PASSWORD="your-app-password"The server refuses to start with neither, naming what to provide.
To create a token: Bitbucket ā Settings ā API tokens (or App passwords for
the legacy mode). Grant only the scopes you need ā repository and
pullrequest cover most usage; add :write variants for the write tools.
Targeting
# Workspace. Optional inside a Bitbucket checkout (read from the git remote),
# required otherwise. There is no built-in default.
export BITBUCKET_WORKSPACE="your-workspace"
# Repo slug. Same rule ā auto-detected inside a checkout.
export BITBUCKET_REPO_SLUG="your-repo"
# Point auto-detection at a directory other than the process cwd.
export BITBUCKET_PROJECT_DIR="/path/to/some/checkout"Safety switches
# Allow non-GET requests through bb_request. Default "true" (reads only).
# Leave it on unless you want the model able to call any write endpoint.
export BITBUCKET_BB_REQUEST_READONLY="false"
# Register bb_delete_repository at all. Default "false".
export BITBUCKET_ALLOW_DESTRUCTIVE="true"The typed write tools (bb_write_file, bb_pr_comment, bb_pr_approve, ā¦) are
not affected by these switches ā they are always available. The switches
govern the generic proxy and repository deletion specifically.
Diagnostics
# Log every request as a runnable curl command. Default "false".
# Read the Security section before enabling this.
export BITBUCKET_ENABLE_REQUEST_LOGGING="true"
# Where to write it. Default: ./bitbucket_requests.log, relative to the cwd.
export BITBUCKET_REQUEST_LOG_FILE="/tmp/bitbucket_requests.log"Tools
23 tools. Two cover the entire REST API; the rest are typed conveniences for the
highest-traffic operations. workspace and repo_slug are optional wherever
they appear ā see auto-detection.
Generic API access
Tool | What it does |
| Search all 294 endpoints for the right method, path and params. Every whitespace-separated term must match, so |
| Call any Bitbucket Cloud 2.0 endpoint. The escape hatch for merges, commit comparison, branches and tags, pipelines, code search, webhooks, permissions. |
The intended flow is discover, then call:
bb_list_endpoints(search="comment pull request", method="POST")
-> POST /repositories/{workspace}/{repo_slug}/pullrequests/{pull_request_id}/comments
bb_request(method="POST",
path="/repositories/myworkspace/myrepo/pullrequests/123/comments",
body={"content": {"raw": "Looks good"},
"inline": {"path": "src/example.py", "to": 42}})paginate=true follows Bitbucket's next links and merges every page's
values, capped by max_pages. raw=true returns the body verbatim, which is
what the diff, patch and pipeline-log endpoints need since they answer in
text/plain. The fields query param trims large responses and materially cuts
token cost.
Pull requests and review
Tool | What it does |
| List PRs by state, source/destination branch or author. The |
| PR details, optionally with comments, commits and diff. |
| Open a PR from a source branch. |
| Per-file added/removed counts without the diff bodies. |
| One file's diff out of the combined PR diff. |
| The PR diff split per file. |
| A file's full content at the PR's source commit. |
| Comments, flagging inline location, replies and resolved threads. |
| Comment on a PR. |
| Mark a comment thread resolved. |
| Approve, as the authenticated user. |
| Request changes, as the authenticated user. |
The last five are visible to your team and mostly irreversible. The server tells the model to confirm intent first, but that is guidance, not enforcement.
Repositories, source and issues
Tool | What it does |
| Search with Bitbucket's query syntax, e.g. |
| Create a repository. |
| Create a branch from a start point. |
| Read a file at a branch or commit. |
| Create or update a file, with a commit message. |
| Delete a file. |
| Create an issue with kind and priority. |
| Delete an issue. |
| Delete a repository. Not registered unless |
Repository auto-detection
The server reads the workspace and repo slug from the git remote of its working
directory, so one user-level registration serves every Bitbucket checkout ā
repo_slug becomes optional and defaults to whichever repo the session was
opened in. Both SSH and HTTPS remote forms are recognised.
Precedence is explicit env ā detected ā unset:
Setting | Env override | Detected from | If neither |
Workspace |
|
| required per call |
Repo slug |
|
| required per call |
There is deliberately no fallback workspace. When nothing resolves, calls
fail with an explicit "pass workspace and repo_slug explicitly, or set
BITBUCKET_WORKSPACE" message rather than an opaque 404 ā or worse, a silent
request against somebody else's workspace.
Endpoint index
bb_list_endpoints reads src/mcp_bitbucket/endpoints.json, generated from the
same OpenAPI spec that renders
the official REST docs.
Each entry carries the method, path, tag, summary, path/query parameters, whether
it accepts a body, and a deep link to its docs section.
Regenerate it when Atlassian ships API changes:
# Uses a cached copy of the spec if it is less than 24h old
uv run python scripts/generate_endpoints.py
# Bypass the cache entirely
uv run python scripts/generate_endpoints.py --force
# Revalidate against the server instead of trusting the TTL
uv run python scripts/generate_endpoints.py --max-age 0The spec is cached under .spec-cache/ with its ETag. A TTL gate keeps repeat
runs off the network; ETag revalidation is attempted too, but the dac-static
CDN drops its ETag on the gzip variant it serves (it sets
Vary: Accept-Encoding), so only the api.bitbucket.org/swagger.json mirror can
actually answer 304. The generator refuses to overwrite the index if the spec
yields zero operations, and skips the write entirely when nothing changed.
Security
Worth understanding before you point this at a repository you care about.
The working directory decides the target. Auto-detection runs at import time
against the process cwd, which the client sets to your project. That is what
makes one registration work everywhere, but it also means the model can act on
whichever repo the session was opened in without naming it. Set
BITBUCKET_WORKSPACE / BITBUCKET_REPO_SLUG explicitly if you want a fixed
target.
bb_request reaches every endpoint. With BITBUCKET_BB_REQUEST_READONLY
at its default of true it refuses everything but GET. Turning it off grants
the model any write your credential can perform, including endpoints no typed
tool wraps.
Scope your credential. The server enforces nothing about permissions ā the token does. A read-scoped token is the strongest available guard rail.
Request logging writes bodies to disk. When
BITBUCKET_ENABLE_REQUEST_LOGGING is on, each request is appended as a runnable
curl command. Credentials are redacted ā the Authorization header and both
halves of basic auth ā but request bodies are written verbatim, which for
bb_write_file means file contents and for bb_pr_comment means comment text.
The file is relative to the cwd by default, so it lands inside your project and
grows unrotated. It is off by default for these reasons; review a log before
sharing it.
Tool annotations are hints. Every tool carries readOnlyHint /
destructiveHint so your client can prompt appropriately, and
bb_delete_repository is withheld from the schema entirely unless enabled.
These help a well-behaved client; they are not server-side enforcement.
To report a vulnerability, see SECURITY.md.
Development
Running tests
# Everything that runs offline (fully mocked, no credentials, no network)
python -m unittest tests.test_bb_api tests.test_auth_modes tests.test_stdio_handshake
# Lint and format
uv run ruff check .
uv run ruff format .tests/test_bb_integration.py is excluded above deliberately: it hits the
real Bitbucket API and creates and deletes real repositories. It skips
unless credentials and BITBUCKET_TEST_WORKSPACE are set. See
tests/README.md.
Adding a tool
Pick the module under
src/mcp_bitbucket/tools/by API area, or add one and register it intools/__init__.py.Inside
register(server), add@server.tool(annotations=..., description=...)on anasync def bb_*with keyword-only params, eachAnnotated[T, Field(description=...)]. The signature is the JSON Schema.Default
workspace/repo_slugto theconfigvalues.Call through
http_client.request()+api_url(), thenrequire_ok().Return trimmed, human-readable text ā every token lands in someone's context.
Descriptions are prompt engineering, not documentation: they are the only thing the model sees when choosing a tool.
Project structure
src/mcp_bitbucket/
āāā server.py # MCPServer assembly + stdio entry point
āāā config.py # environment-derived settings, repo auto-detection
āāā http_client.py # auth, curl logging, error shaping, pagination
āāā instructions.py # instructions sent to the client on initialize
āāā annotations.py # MCP tool annotations (read-only / destructive)
āāā endpoint_index.py # loads and searches endpoints.json
āāā endpoints.json # generated index of all 294 endpoints
āāā diffs.py # unified-diff slicing
āāā tools/
āāā generic.py # bb_list_endpoints / bb_request
āāā pr_review.py # curated review tools
āāā pullrequests.py
āāā repositories.py
āāā source.py
āāā issues.py
scripts/generate_endpoints.py # rebuilds endpoints.json from Atlassian's spec
tests/ # 3 offline suites + 1 credential-gated suiteImplementation notes
Built on MCPServer from the official MCP Python SDK (mcp>=2.1.1) ā the
high-level API formerly called FastMCP, renamed in SDK v2. Tool schemas are
derived from type hints rather than hand-written, ToolError carries readable
failures back to the model, and MCPServer.run owns the stdio transport and the
initialize handshake.
Because stdout is the MCP transport, nothing may be printed there ā diagnostics
go to stderr. tests/test_stdio_handshake.py spawns the server as a real
subprocess and asserts every stdout line is parseable JSON, because a broken
entry point is invisible to in-process tests.
License
MIT.
Copyright (c) 2025 Kevin Kreger (Kallows) for the original work, and copyright (c) 2026 Mohit Goel for the modifications.
Credits & Acknowledgments
This project combines concepts and code from several open-source projects:
Kallows/mcp-bitbucket by Kevin Kreger ā the original Bitbucket MCP server (MIT License). It established the tool surface this still builds on, and the upstream tool names are retained for compatibility. Thank you for publishing it.
MCP Python SDK ā server framework, transport and the
MCPServertool API (MIT License).Atlassian Bitbucket Cloud OpenAPI specification ā the source the bundled 294-endpoint index in
endpoints.jsonis generated from, which is why it cannot drift from the published docs.
What changed from the original
Migrated from the low-level
ServerSDK API ā hand-written JSON schemas and a singlecall_tooldispatch ā toMCPServer, with schemas derived from typed signatures.Split one 856-line module into focused modules with a shared HTTP layer.
Added the generated 294-endpoint index and the
bb_list_endpoints/bb_requestpair, taking coverage from 10 endpoints to the whole API.Added the pull request review toolset, per-file diff handling, and response trimming.
Added workspace/repo auto-detection from the git remote, scoped-token auth, redacted request logging, pagination, tool annotations and the safety switches.
Contributing
Contributions are welcome. Please:
Open an issue first for anything substantial.
Keep the offline test suites green, and add tests for new behaviour.
Run
ruff check .andruff format ..Update the README when you change a tool's surface or an env variable.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/mohitgoel188/mcp-bitbucket'
If you have feedback or need assistance with the MCP directory API, please join our Discord server