Covers domain client split, write operations (PR, comment, task), and repository/workspace/branch browsing tools. Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
9.5 KiB
Bitbucket MCP Server — Missing Features Design
Date: 2026-05-20
Status: Approved
Overview
Extend the existing read-only Bitbucket MCP server with:
- Repository/workspace browsing — list workspaces, repos, branches; get repo metadata
- PR write operations — create, update, merge, decline, approve, request-changes
- Comment write operations — add, update, delete comments (general and inline)
- Task write operations — create, update, delete PR tasks
The server's public contract (tool names, parameter shapes, ToolResult response envelope) remains unchanged; new tools are additive.
Architecture
The current monolithic bitbucket-client.ts is split into domain modules. A shared base-client.ts owns the Axios instance, initialization guard, error interceptors, and formatError. Domain clients extend or compose the base. BitbucketClient becomes a composition root that wires all domain clients together and exposes a flat API to the router.
src/
├── index.ts # MCP server — tool schemas added here
├── router.ts # Tool dispatcher — new switch cases added
├── config.ts # Unchanged
├── bitbucket-client.ts # Composition root: instantiates & delegates to domain clients
└── clients/
├── base-client.ts # Shared Axios instance, ensureInitialized, formatError, error interceptors
├── pull-request-client.ts # All PR methods (read + write)
├── repository-client.ts # Workspace, repo, branch methods
└── comment-client.ts # Comment + task CRUD
The router continues calling this.client.<method>() — the composition root makes this transparent.
Domain Clients
base-client.ts
Extracted from the current bitbucket-client.ts:
- Holds the
AxiosInstance initializeClient(options)— loads config, creates axios, attaches interceptorsensureInitialized()— polling guard used by all domain clientshandleResponseError(error)— 401/403/429 logging and rate-limit waitformatError(error)— HTTP status + message formattersleep(ms)— utility
All domain clients receive a reference to the base client (or extend it) to share the axios instance and initialization state.
pull-request-client.ts
Existing read methods (migrated from current bitbucket-client.ts):
listPullRequests,getPullRequest,getPullRequestActivitiesgetPullRequestChanges,getPullRequestComments,getPullRequestCommentgetPullRequestCommits,getPullRequestDiff,getPullRequestPatchgetPullRequestParticipants,getPullRequestReviewers,getPullRequestStatusgetPullRequestTasks,getPullRequestTaskCount,getFullPullRequest
New write methods:
| Method | HTTP | Endpoint |
|---|---|---|
createPullRequest(ws, repo, opts) |
POST | /repositories/{ws}/{repo}/pullrequests |
updatePullRequest(ws, repo, id, opts) |
PUT | /repositories/{ws}/{repo}/pullrequests/{id} |
mergePullRequest(ws, repo, id, opts) |
POST | .../pullrequests/{id}/merge |
declinePullRequest(ws, repo, id) |
POST | .../pullrequests/{id}/decline |
approvePullRequest(ws, repo, id) |
POST | .../pullrequests/{id}/approve |
unapprovePullRequest(ws, repo, id) |
DELETE | .../pullrequests/{id}/approve |
requestChangesPullRequest(ws, repo, id) |
POST | .../pullrequests/{id}/request-changes |
removeRequestChangesPullRequest(ws, repo, id) |
DELETE | .../pullrequests/{id}/request-changes |
createPullRequest options:
{
title: string;
source_branch: string;
destination_branch: string;
description?: string;
reviewers?: string[]; // account UUIDs or usernames
close_source_branch?: boolean;
}
mergePullRequest options:
{
merge_strategy?: 'merge_commit' | 'squash' | 'fast_forward';
commit_message?: string;
close_source_branch?: boolean;
}
updatePullRequest options (all optional, at least one required):
{
title?: string;
description?: string;
reviewers?: string[];
destination_branch?: string;
}
repository-client.ts
New domain client — no existing methods to migrate.
| Method | HTTP | Endpoint |
|---|---|---|
listWorkspaces(opts) |
GET | /workspaces |
listRepositories(ws, opts) |
GET | /repositories/{workspace} |
getRepository(ws, repo) |
GET | /repositories/{workspace}/{repo_slug} |
listBranches(ws, repo, opts) |
GET | /repositories/{ws}/{repo}/refs/branches |
listWorkspaces options: page, pagelen
listRepositories options: role (member|contributor|owner), page, pagelen
listBranches options: page, pagelen, filter_by_name (maps to q=name~"<value>")
comment-client.ts
Existing read methods (migrated):
getPullRequestComments,getPullRequestComment
New write methods:
| Method | HTTP | Endpoint |
|---|---|---|
addPullRequestComment(ws, repo, id, opts) |
POST | .../pullrequests/{id}/comments |
updatePullRequestComment(ws, repo, id, commentId, opts) |
PUT | .../comments/{commentId} |
deletePullRequestComment(ws, repo, id, commentId) |
DELETE | .../comments/{commentId} |
createPullRequestTask(ws, repo, id, opts) |
POST | .../pullrequests/{id}/tasks |
updatePullRequestTask(ws, repo, id, taskId, opts) |
PUT | .../tasks/{taskId} |
deletePullRequestTask(ws, repo, id, taskId) |
DELETE | .../tasks/{taskId} |
addPullRequestComment options:
{
content: string;
inline?: { path: string; to: number }; // inline comment on file:line
parent_id?: number; // reply to existing comment
}
updatePullRequestComment options:
{ content: string }
createPullRequestTask options:
{
content: string;
comment_id?: number; // anchor to a specific comment
}
updatePullRequestTask options:
{
content?: string;
state?: 'RESOLVED' | 'UNRESOLVED';
}
New MCP Tools (index.ts)
Repository/Workspace
| Tool name | Required params | Optional params |
|---|---|---|
list_workspaces |
— | page, pagelen |
list_repositories |
workspace |
role, page, pagelen |
get_repository |
workspace, repository |
— |
list_branches |
workspace, repository |
filter_by_name, page, pagelen |
PR Write
| Tool name | Required params | Optional params |
|---|---|---|
create_pull_request |
workspace, repository, title, source_branch, destination_branch |
description, reviewers, close_source_branch |
update_pull_request |
workspace, repository, pullRequestId |
title, description, reviewers, destination_branch |
merge_pull_request |
workspace, repository, pullRequestId |
merge_strategy, commit_message, close_source_branch |
decline_pull_request |
workspace, repository, pullRequestId |
— |
approve_pull_request |
workspace, repository, pullRequestId |
— |
unapprove_pull_request |
workspace, repository, pullRequestId |
— |
request_changes_pull_request |
workspace, repository, pullRequestId |
— |
remove_request_changes_pull_request |
workspace, repository, pullRequestId |
— |
Comment Write
| Tool name | Required params | Optional params |
|---|---|---|
add_pull_request_comment |
workspace, repository, pullRequestId, content |
inline_path, inline_line, parent_comment_id |
update_pull_request_comment |
workspace, repository, pullRequestId, commentId, content |
— |
delete_pull_request_comment |
workspace, repository, pullRequestId, commentId |
— |
Task Write
| Tool name | Required params | Optional params |
|---|---|---|
create_pull_request_task |
workspace, repository, pullRequestId, content |
comment_id |
update_pull_request_task |
workspace, repository, pullRequestId, taskId |
content, state |
delete_pull_request_task |
workspace, repository, pullRequestId, taskId |
— |
Router (router.ts)
New switch cases added to executeTool() for every new tool. Same pattern as existing cases:
- Extract
workspace,repoSlugviagetDefaultParams() - Parse integer IDs
- Validate required params — return
{ success: false, error: '...' }if missing - Delegate to
this.client.<method>() - Return
{ success: true, data: result }
No new helper is needed for write ops; the existing pattern is clear enough.
Error Handling
- Write operations do not retry on failure.
base-client.tserror interceptor handles 401/403/429 (existing behavior preserved).- All domain client methods catch errors and re-throw with
this.formatError(error). - Router catches and returns
{ success: false, error: '...' }.
Testing
Unit tests (tests/unit/router.test.ts):
- New cases added for every new tool, mocking
BitbucketClientidentically to existing tests.
Integration tests (tests/integration/bitbucket-api.test.ts):
- Read operations (workspaces, repos, branches) added unconditionally.
- Write operations (merge, decline, approve, comment, task) gated on
RUN_WRITE_TESTS=trueenv var to prevent accidental mutation of real PRs.
Out of Scope
- Webhooks, pipelines, deployments, issue tracker, snippets.
- Any Bitbucket Server (self-hosted) support — this server targets Bitbucket Cloud only.
- Streaming / SSE responses.