bitbucket-mcp
Version:
Model Context Protocol (MCP) server for Bitbucket Cloud and Server API integration
1,049 lines (1,048 loc) • 152 kB
JavaScript
#!/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 from "axios";
import winston from "winston";
import os from "os";
import path from "path";
import fs from "fs";
import { BitbucketPaginator, BITBUCKET_ALL_ITEMS_CAP, BITBUCKET_DEFAULT_PAGELEN, BITBUCKET_MAX_PAGELEN, } from "./pagination.js";
// =========== LOGGER SETUP ==========
// File-based logging with sensible defaults and ability to disable
function getDefaultLogDirectory() {
if (process.platform === "win32") {
const base = process.env.LOCALAPPDATA || path.join(os.homedir(), "AppData", "Local");
return path.join(base, "bitbucket-mcp");
}
if (process.platform === "darwin") {
return path.join(os.homedir(), "Library", "Logs", "bitbucket-mcp");
}
const xdgStateHome = process.env.XDG_STATE_HOME;
if (xdgStateHome && xdgStateHome.length > 0) {
return path.join(xdgStateHome, "bitbucket-mcp");
}
return path.join(os.homedir(), ".local", "state", "bitbucket-mcp");
}
function isTruthyEnv(value) {
if (value === undefined || value === null)
return false;
const normalized = String(value).toLowerCase();
return ["1", "true", "yes", "on"].includes(normalized);
}
function getLogFilePath() {
if (isTruthyEnv(process.env.BITBUCKET_LOG_DISABLE)) {
return undefined;
}
const explicitFile = process.env.BITBUCKET_LOG_FILE;
if (explicitFile && explicitFile.trim().length > 0) {
return explicitFile;
}
const baseDir = process.env.BITBUCKET_LOG_DIR &&
process.env.BITBUCKET_LOG_DIR.trim().length > 0
? process.env.BITBUCKET_LOG_DIR
: getDefaultLogDirectory();
let effectiveDir = baseDir;
if (isTruthyEnv(process.env.BITBUCKET_LOG_PER_CWD)) {
const sanitizedCwd = process
.cwd()
.replace(/[\\/]/g, "_")
.replace(/[:*?"<>|]/g, "");
effectiveDir = path.join(baseDir, sanitizedCwd);
}
try {
fs.mkdirSync(effectiveDir, { recursive: true });
}
catch {
return undefined; // If we cannot create the directory, disable file logging rather than polluting CWD
}
return path.join(effectiveDir, "bitbucket.log");
}
const resolvedLogFile = getLogFilePath();
const logger = winston.createLogger({
level: "info",
format: winston.format.json(),
transports: resolvedLogFile
? [new winston.transports.File({ filename: resolvedLogFile })]
: [],
});
const PAGINATION_BASE_SCHEMA = {
pagelen: {
type: "number",
minimum: 1,
maximum: BITBUCKET_MAX_PAGELEN,
description: `Number of items per page (Bitbucket pagelen). Defaults to ${BITBUCKET_DEFAULT_PAGELEN} and caps at ${BITBUCKET_MAX_PAGELEN}.`,
},
page: {
type: "number",
minimum: 1,
description: "Bitbucket page number to fetch (1-based).",
},
};
const PAGINATION_ALL_SCHEMA = {
type: "boolean",
description: `When true (and no page is provided), automatically follows Bitbucket next links to return all items up to ${BITBUCKET_ALL_ITEMS_CAP}.`,
};
const LEGACY_LIMIT_SCHEMA = {
type: "number",
description: "Deprecated alias for pagelen. Use pagelen/page/all for pagination control.",
};
// Normalize Bitbucket configuration for backward compatibility and DX
function normalizeBitbucketConfig(rawConfig) {
let normalizedConfig = { ...rawConfig };
try {
const parsed = new URL(rawConfig.baseUrl);
const host = parsed.hostname.toLowerCase();
// If users provide a web URL like https://bitbucket.org/<workspace>,
// extract the workspace and switch to the public API base URL
if (host === "bitbucket.org" || host === "www.bitbucket.org") {
const segments = parsed.pathname.split("/").filter(Boolean);
if (!normalizedConfig.defaultWorkspace && segments.length >= 1) {
normalizedConfig.defaultWorkspace = segments[0];
}
normalizedConfig.baseUrl = "https://api.bitbucket.org/2.0";
}
// If users provide https://api.bitbucket.org (without /2.0), ensure /2.0
if (host === "api.bitbucket.org") {
const pathname = parsed.pathname.replace(/\/+$/, "");
if (!pathname.startsWith("/2.0")) {
normalizedConfig.baseUrl = "https://api.bitbucket.org/2.0";
}
else {
normalizedConfig.baseUrl = "https://api.bitbucket.org/2.0";
}
}
// Remove trailing slashes for a consistent axios baseURL
normalizedConfig.baseUrl = normalizedConfig.baseUrl.replace(/\/+$/, "");
}
catch {
// If baseUrl is not a valid absolute URL, keep as-is (custom/self-hosted cases)
}
return normalizedConfig;
}
// =========== MCP SERVER ===========
class BitbucketServer {
isDangerousTool(name) {
// Explicitly dangerous or conservative prefix match (delete*)
if (this.dangerousToolNames.has(name))
return true;
if (/^delete/i.test(name))
return true;
return false;
}
constructor() {
this.dangerousToolNames = new Set([
"deletePullRequestComment",
"deletePullRequestTask",
]);
// Initialize with the older Server class pattern
this.server = new Server({
name: "bitbucket-mcp-server",
version: "1.0.0",
}, {
capabilities: {
tools: {},
},
});
// Configuration from environment variables
const initialConfig = {
baseUrl: process.env.BITBUCKET_URL ?? "https://api.bitbucket.org/2.0",
token: process.env.BITBUCKET_TOKEN,
username: process.env.BITBUCKET_USERNAME,
password: process.env.BITBUCKET_PASSWORD,
defaultWorkspace: process.env.BITBUCKET_WORKSPACE,
};
const normalizedConfig = normalizeBitbucketConfig(initialConfig);
if (normalizedConfig.baseUrl !== initialConfig.baseUrl ||
normalizedConfig.defaultWorkspace !== initialConfig.defaultWorkspace) {
logger.info("Normalized Bitbucket configuration", {
fromBaseUrl: initialConfig.baseUrl,
toBaseUrl: normalizedConfig.baseUrl,
defaultWorkspace: normalizedConfig.defaultWorkspace,
});
}
// Parse dangerous commands toggle (off by default)
const enableDangerousEnv = (process.env.BITBUCKET_ENABLE_DANGEROUS ??
process.env.BITBUCKET_ALLOW_DANGEROUS ??
"")
.toString()
.toLowerCase();
const allowDangerousCommands = ["1", "true", "yes", "on"].includes(enableDangerousEnv);
this.config = { ...normalizedConfig, allowDangerousCommands };
// Validate required config
if (!this.config.baseUrl) {
throw new Error("BITBUCKET_URL is required");
}
if (!this.config.token && !(this.config.username && this.config.password)) {
throw new Error("Either BITBUCKET_TOKEN or BITBUCKET_USERNAME/PASSWORD is required");
}
// Setup Axios instance
this.api = axios.create({
baseURL: this.config.baseUrl,
headers: this.config.token
? { Authorization: `Bearer ${this.config.token}` }
: { "Content-Type": "application/json" },
auth: this.config.username && this.config.password
? { username: this.config.username, password: this.config.password }
: undefined,
});
this.paginator = new BitbucketPaginator(this.api, logger);
// Setup tool handlers using the request handler pattern
this.setupToolHandlers();
// Add error handler - CRITICAL for stability
this.server.onerror = (error) => logger.error("[MCP Error]", error);
}
setupToolHandlers() {
// Register the list tools handler
this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
tools: [
{
name: "listRepositories",
description: "List Bitbucket repositories",
inputSchema: {
type: "object",
properties: {
workspace: {
type: "string",
description: "Bitbucket workspace name",
},
name: {
type: "string",
description: "Filter repositories by name (partial match supported)",
},
...PAGINATION_BASE_SCHEMA,
all: PAGINATION_ALL_SCHEMA,
limit: LEGACY_LIMIT_SCHEMA,
},
},
},
{
name: "getRepository",
description: "Get repository details",
inputSchema: {
type: "object",
properties: {
workspace: {
type: "string",
description: "Bitbucket workspace name",
},
repo_slug: { type: "string", description: "Repository slug" },
},
required: ["workspace", "repo_slug"],
},
},
{
name: "getPullRequests",
description: "Get pull requests for a repository",
inputSchema: {
type: "object",
properties: {
workspace: {
type: "string",
description: "Bitbucket workspace name",
},
repo_slug: { type: "string", description: "Repository slug" },
state: {
type: "string",
enum: ["OPEN", "MERGED", "DECLINED", "SUPERSEDED"],
description: "Pull request state",
},
...PAGINATION_BASE_SCHEMA,
all: PAGINATION_ALL_SCHEMA,
limit: LEGACY_LIMIT_SCHEMA,
},
required: ["workspace", "repo_slug"],
},
},
{
name: "createPullRequest",
description: "Create a new pull request",
inputSchema: {
type: "object",
properties: {
workspace: {
type: "string",
description: "Bitbucket workspace name",
},
repo_slug: { type: "string", description: "Repository slug" },
title: { type: "string", description: "Pull request title" },
description: {
type: "string",
description: "Pull request description",
},
sourceBranch: {
type: "string",
description: "Source branch name",
},
targetBranch: {
type: "string",
description: "Target branch name",
},
reviewers: {
type: "array",
items: { type: "string" },
description: "List of reviewer UUIDs (e.g., '{04776764-62c7-453b-b97e-302f60395ceb}')",
},
draft: {
type: "boolean",
description: "Whether to create the pull request as a draft",
},
},
required: [
"workspace",
"repo_slug",
"title",
"description",
"sourceBranch",
"targetBranch",
],
},
},
{
name: "getPullRequest",
description: "Get details for a specific pull request",
inputSchema: {
type: "object",
properties: {
workspace: {
type: "string",
description: "Bitbucket workspace name",
},
repo_slug: { type: "string", description: "Repository slug" },
pull_request_id: {
type: "string",
description: "Pull request ID",
},
...PAGINATION_BASE_SCHEMA,
all: PAGINATION_ALL_SCHEMA,
},
required: ["workspace", "repo_slug", "pull_request_id"],
},
},
{
name: "updatePullRequest",
description: "Update a pull request",
inputSchema: {
type: "object",
properties: {
workspace: {
type: "string",
description: "Bitbucket workspace name",
},
repo_slug: { type: "string", description: "Repository slug" },
pull_request_id: {
type: "string",
description: "Pull request ID",
},
title: { type: "string", description: "New pull request title" },
description: {
type: "string",
description: "New pull request description",
},
},
required: ["workspace", "repo_slug", "pull_request_id"],
},
},
{
name: "getPullRequestActivity",
description: "Get activity log for a pull request",
inputSchema: {
type: "object",
properties: {
workspace: {
type: "string",
description: "Bitbucket workspace name",
},
repo_slug: { type: "string", description: "Repository slug" },
pull_request_id: {
type: "string",
description: "Pull request ID",
},
...PAGINATION_BASE_SCHEMA,
all: PAGINATION_ALL_SCHEMA,
},
required: ["workspace", "repo_slug", "pull_request_id"],
},
},
{
name: "approvePullRequest",
description: "Approve a pull request",
inputSchema: {
type: "object",
properties: {
workspace: {
type: "string",
description: "Bitbucket workspace name",
},
repo_slug: { type: "string", description: "Repository slug" },
pull_request_id: {
type: "string",
description: "Pull request ID",
},
...PAGINATION_BASE_SCHEMA,
all: PAGINATION_ALL_SCHEMA,
},
required: ["workspace", "repo_slug", "pull_request_id"],
},
},
{
name: "unapprovePullRequest",
description: "Remove approval from a pull request",
inputSchema: {
type: "object",
properties: {
workspace: {
type: "string",
description: "Bitbucket workspace name",
},
repo_slug: { type: "string", description: "Repository slug" },
pull_request_id: {
type: "string",
description: "Pull request ID",
},
...PAGINATION_BASE_SCHEMA,
all: PAGINATION_ALL_SCHEMA,
},
required: ["workspace", "repo_slug", "pull_request_id"],
},
},
{
name: "declinePullRequest",
description: "Decline a pull request",
inputSchema: {
type: "object",
properties: {
workspace: {
type: "string",
description: "Bitbucket workspace name",
},
repo_slug: { type: "string", description: "Repository slug" },
pull_request_id: {
type: "string",
description: "Pull request ID",
},
message: { type: "string", description: "Reason for declining" },
},
required: ["workspace", "repo_slug", "pull_request_id"],
},
},
{
name: "mergePullRequest",
description: "Merge a pull request",
inputSchema: {
type: "object",
properties: {
workspace: {
type: "string",
description: "Bitbucket workspace name",
},
repo_slug: { type: "string", description: "Repository slug" },
pull_request_id: {
type: "string",
description: "Pull request ID",
},
message: { type: "string", description: "Merge commit message" },
strategy: {
type: "string",
enum: ["merge-commit", "squash", "fast-forward"],
description: "Merge strategy",
},
},
required: ["workspace", "repo_slug", "pull_request_id"],
},
},
{
name: "getPullRequestComments",
description: "List comments on a pull request",
inputSchema: {
type: "object",
properties: {
workspace: {
type: "string",
description: "Bitbucket workspace name",
},
repo_slug: { type: "string", description: "Repository slug" },
pull_request_id: {
type: "string",
description: "Pull request ID",
},
...PAGINATION_BASE_SCHEMA,
all: PAGINATION_ALL_SCHEMA,
},
required: ["workspace", "repo_slug", "pull_request_id"],
},
},
{
name: "getPullRequestDiff",
description: "Get diff for a pull request",
inputSchema: {
type: "object",
properties: {
workspace: {
type: "string",
description: "Bitbucket workspace name",
},
repo_slug: { type: "string", description: "Repository slug" },
pull_request_id: {
type: "string",
description: "Pull request ID",
},
},
required: ["workspace", "repo_slug", "pull_request_id"],
},
},
{
name: "getPullRequestCommits",
description: "Get commits on a pull request",
inputSchema: {
type: "object",
properties: {
workspace: {
type: "string",
description: "Bitbucket workspace name",
},
repo_slug: { type: "string", description: "Repository slug" },
pull_request_id: {
type: "string",
description: "Pull request ID",
},
...PAGINATION_BASE_SCHEMA,
all: PAGINATION_ALL_SCHEMA,
},
required: ["workspace", "repo_slug", "pull_request_id"],
},
},
{
name: "addPullRequestComment",
description: "Add a comment to a pull request (general or inline)",
inputSchema: {
type: "object",
properties: {
workspace: {
type: "string",
description: "Bitbucket workspace name",
},
repo_slug: { type: "string", description: "Repository slug" },
pull_request_id: {
type: "string",
description: "Pull request ID",
},
content: {
type: "string",
description: "Comment content in markdown format",
},
pending: {
type: "boolean",
description: "Whether to create this comment as a pending comment (draft state)",
},
inline: {
type: "object",
description: "Inline comment information for commenting on specific lines",
properties: {
path: {
type: "string",
description: "Path to the file in the repository",
},
from: {
type: "number",
description: "Line number in the old version of the file (for deleted or modified lines)",
},
to: {
type: "number",
description: "Line number in the new version of the file (for added or modified lines)",
},
},
required: ["path"],
},
},
required: ["workspace", "repo_slug", "pull_request_id", "content"],
},
},
{
name: "addPendingPullRequestComment",
description: "Add a pending (draft) comment to a pull request that can be published later",
inputSchema: {
type: "object",
properties: {
workspace: {
type: "string",
description: "Bitbucket workspace name",
},
repo_slug: { type: "string", description: "Repository slug" },
pull_request_id: {
type: "string",
description: "Pull request ID",
},
content: {
type: "string",
description: "Comment content in markdown format",
},
inline: {
type: "object",
description: "Inline comment information for commenting on specific lines",
properties: {
path: {
type: "string",
description: "Path to the file in the repository",
},
from: {
type: "number",
description: "Line number in the old version of the file (for deleted or modified lines)",
},
to: {
type: "number",
description: "Line number in the new version of the file (for added or modified lines)",
},
},
required: ["path"],
},
},
required: ["workspace", "repo_slug", "pull_request_id", "content"],
},
},
{
name: "publishPendingComments",
description: "Publish all pending comments for a pull request",
inputSchema: {
type: "object",
properties: {
workspace: {
type: "string",
description: "Bitbucket workspace name",
},
repo_slug: { type: "string", description: "Repository slug" },
pull_request_id: {
type: "string",
description: "Pull request ID",
},
},
required: ["workspace", "repo_slug", "pull_request_id"],
},
},
{
name: "getRepositoryBranchingModel",
description: "Get the branching model for a repository",
inputSchema: {
type: "object",
properties: {
workspace: {
type: "string",
description: "Bitbucket workspace name",
},
repo_slug: { type: "string", description: "Repository slug" },
},
required: ["workspace", "repo_slug"],
},
},
{
name: "getRepositoryBranchingModelSettings",
description: "Get the branching model config for a repository",
inputSchema: {
type: "object",
properties: {
workspace: {
type: "string",
description: "Bitbucket workspace name",
},
repo_slug: { type: "string", description: "Repository slug" },
},
required: ["workspace", "repo_slug"],
},
},
{
name: "updateRepositoryBranchingModelSettings",
description: "Update the branching model config for a repository",
inputSchema: {
type: "object",
properties: {
workspace: {
type: "string",
description: "Bitbucket workspace name",
},
repo_slug: { type: "string", description: "Repository slug" },
development: {
type: "object",
description: "Development branch settings",
properties: {
name: { type: "string", description: "Branch name" },
use_mainbranch: {
type: "boolean",
description: "Use main branch",
},
},
},
production: {
type: "object",
description: "Production branch settings",
properties: {
name: { type: "string", description: "Branch name" },
use_mainbranch: {
type: "boolean",
description: "Use main branch",
},
enabled: {
type: "boolean",
description: "Enable production branch",
},
},
},
branch_types: {
type: "array",
description: "Branch types configuration",
items: {
type: "object",
properties: {
kind: {
type: "string",
description: "Branch type kind (e.g., bugfix, feature)",
},
prefix: { type: "string", description: "Branch prefix" },
enabled: {
type: "boolean",
description: "Enable this branch type",
},
},
required: ["kind"],
},
},
},
required: ["workspace", "repo_slug"],
},
},
{
name: "getEffectiveRepositoryBranchingModel",
description: "Get the effective branching model for a repository",
inputSchema: {
type: "object",
properties: {
workspace: {
type: "string",
description: "Bitbucket workspace name",
},
repo_slug: { type: "string", description: "Repository slug" },
},
required: ["workspace", "repo_slug"],
},
},
{
name: "getProjectBranchingModel",
description: "Get the branching model for a project",
inputSchema: {
type: "object",
properties: {
workspace: {
type: "string",
description: "Bitbucket workspace name",
},
project_key: { type: "string", description: "Project key" },
},
required: ["workspace", "project_key"],
},
},
{
name: "getProjectBranchingModelSettings",
description: "Get the branching model config for a project",
inputSchema: {
type: "object",
properties: {
workspace: {
type: "string",
description: "Bitbucket workspace name",
},
project_key: { type: "string", description: "Project key" },
},
required: ["workspace", "project_key"],
},
},
{
name: "updateProjectBranchingModelSettings",
description: "Update the branching model config for a project",
inputSchema: {
type: "object",
properties: {
workspace: {
type: "string",
description: "Bitbucket workspace name",
},
project_key: { type: "string", description: "Project key" },
development: {
type: "object",
description: "Development branch settings",
properties: {
name: { type: "string", description: "Branch name" },
use_mainbranch: {
type: "boolean",
description: "Use main branch",
},
},
},
production: {
type: "object",
description: "Production branch settings",
properties: {
name: { type: "string", description: "Branch name" },
use_mainbranch: {
type: "boolean",
description: "Use main branch",
},
enabled: {
type: "boolean",
description: "Enable production branch",
},
},
},
branch_types: {
type: "array",
description: "Branch types configuration",
items: {
type: "object",
properties: {
kind: {
type: "string",
description: "Branch type kind (e.g., bugfix, feature)",
},
prefix: { type: "string", description: "Branch prefix" },
enabled: {
type: "boolean",
description: "Enable this branch type",
},
},
required: ["kind"],
},
},
},
required: ["workspace", "project_key"],
},
},
{
name: "createDraftPullRequest",
description: "Create a new draft pull request",
inputSchema: {
type: "object",
properties: {
workspace: {
type: "string",
description: "Bitbucket workspace name",
},
repo_slug: { type: "string", description: "Repository slug" },
title: { type: "string", description: "Pull request title" },
description: {
type: "string",
description: "Pull request description",
},
sourceBranch: {
type: "string",
description: "Source branch name",
},
targetBranch: {
type: "string",
description: "Target branch name",
},
reviewers: {
type: "array",
items: { type: "string" },
description: "List of reviewer UUIDs (e.g., '{04776764-62c7-453b-b97e-302f60395ceb}')",
},
},
required: [
"workspace",
"repo_slug",
"title",
"description",
"sourceBranch",
"targetBranch",
],
},
},
{
name: "publishDraftPullRequest",
description: "Publish a draft pull request to make it ready for review",
inputSchema: {
type: "object",
properties: {
workspace: {
type: "string",
description: "Bitbucket workspace name",
},
repo_slug: { type: "string", description: "Repository slug" },
pull_request_id: {
type: "string",
description: "Pull request ID",
},
},
required: ["workspace", "repo_slug", "pull_request_id"],
},
},
{
name: "convertTodraft",
description: "Convert a regular pull request to draft status",
inputSchema: {
type: "object",
properties: {
workspace: {
type: "string",
description: "Bitbucket workspace name",
},
repo_slug: { type: "string", description: "Repository slug" },
pull_request_id: {
type: "string",
description: "Pull request ID",
},
},
required: ["workspace", "repo_slug", "pull_request_id"],
},
},
{
name: "getPendingReviewPRs",
description: "List all open pull requests in the workspace where the authenticated user is a reviewer and has not yet approved.",
inputSchema: {
type: "object",
properties: {
workspace: {
type: "string",
description: "Bitbucket workspace name (optional, defaults to BITBUCKET_WORKSPACE)",
},
limit: {
type: "number",
description: "Maximum number of PRs to return (optional)",
},
repositoryList: {
type: "array",
items: { type: "string" },
description: "List of repository slugs to check (optional)",
},
},
},
},
{
name: "listPipelineRuns",
description: "List pipeline runs for a repository",
inputSchema: {
type: "object",
properties: {
workspace: {
type: "string",
description: "Bitbucket workspace name",
},
repo_slug: { type: "string", description: "Repository slug" },
...PAGINATION_BASE_SCHEMA,
all: PAGINATION_ALL_SCHEMA,
limit: LEGACY_LIMIT_SCHEMA,
status: {
type: "string",
enum: [
"PENDING",
"IN_PROGRESS",
"SUCCESSFUL",
"FAILED",
"ERROR",
"STOPPED",
],
description: "Filter pipelines by status",
},
target_branch: {
type: "string",
description: "Filter pipelines by target branch",
},
trigger_type: {
type: "string",
enum: ["manual", "push", "pullrequest", "schedule"],
description: "Filter pipelines by trigger type",
},
},
required: ["workspace", "repo_slug"],
},
},
{
name: "getPipelineRun",
description: "Get details for a specific pipeline run",
inputSchema: {
type: "object",
properties: {
workspace: {
type: "string",
description: "Bitbucket workspace name",
},
repo_slug: { type: "string", description: "Repository slug" },
pipeline_uuid: {
type: "string",
description: "Pipeline UUID",
},
...PAGINATION_BASE_SCHEMA,
all: PAGINATION_ALL_SCHEMA,
},
required: ["workspace", "repo_slug", "pipeline_uuid"],
},
},
{
name: "runPipeline",
description: "Trigger a new pipeline run",
inputSchema: {
type: "object",
properties: {
workspace: {
type: "string",
description: "Bitbucket workspace name",
},
repo_slug: { type: "string", description: "Repository slug" },
target: {
type: "object",
description: "Pipeline target configuration",
properties: {
ref_type: {
type: "string",
enum: ["branch", "tag", "bookmark", "named_branch"],
description: "Reference type",
},
ref_name: {
type: "string",
description: "Reference name (branch, tag, etc.)",
},
commit_hash: {
type: "string",
description: "Specific commit hash to run pipeline on",
},
selector_type: {
type: "string",
enum: ["default", "custom", "pull-requests"],
description: "Pipeline selector type",
},
selector_pattern: {
type: "string",
description: "Pipeline selector pattern (for custom pipelines)",
},
},
required: ["ref_type", "ref_name"],
},
variables: {
type: "array",
description: "Pipeline variables",
items: {
type: "object",
properties: {
key: { type: "string", description: "Variable name" },
value: { type: "string", description: "Variable value" },
secured: {
type: "boolean",
description: "Whether the variable is secured",
},
},
required: ["key", "value"],
},
},
},
required: ["workspace", "repo_slug", "target"],
},
},
{
name: "stopPipeline",
description: "Stop a running pipeline",
inputSchema: {
type: "object",
properties: {
workspace: {
type: "string",
description: "Bitbucket workspace name",
},
repo_slug: { type: "string", description: "Repository slug" },
pipeline_uuid: {