We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/Acendas/bitbucket-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server
"""Pipeline (CI/CD) tools for Bitbucket MCP Server."""
from typing import Optional
import httpx
from bitbucket_mcp.server import mcp, get_auth, get_workspace, BITBUCKET_API
@mcp.tool()
def list_pipelines(
repo_slug: str,
sort: Optional[str] = None,
workspace: Optional[str] = None,
page: int = 1,
pagelen: int = 25
) -> dict:
"""
List pipelines for a repository.
Args:
repo_slug: Repository slug (name)
sort: Sort field (e.g., "-created_on" for newest first) (optional)
workspace: Bitbucket workspace (optional if configured)
page: Page number for pagination (default: 1)
pagelen: Number of results per page, max 100 (default: 25)
Returns:
List of pipelines with their details
"""
try:
ws = get_workspace(workspace)
auth = get_auth()
params = {
"page": page,
"pagelen": min(pagelen, 100)
}
if sort:
params["sort"] = sort
with httpx.Client() as client:
response = client.get(
f"{BITBUCKET_API}/repositories/{ws}/{repo_slug}/pipelines",
auth=auth,
params=params,
timeout=30.0
)
if response.status_code == 200:
data = response.json()
pipelines = []
for pipeline in data.get("values", []):
target = pipeline.get("target", {})
trigger = pipeline.get("trigger", {})
state = pipeline.get("state", {})
state_result = state.get("result", {})
pipelines.append({
"uuid": pipeline.get("uuid"),
"build_number": pipeline.get("build_number"),
"state": state.get("name"),
"result": state_result.get("name") if state_result else None,
"branch": target.get("ref_name") or target.get("source"),
"commit_hash": target.get("commit", {}).get("hash", "")[:7] if target.get("commit") else None,
"trigger_type": trigger.get("type"),
"created_on": pipeline.get("created_on"),
"completed_on": pipeline.get("completed_on"),
"duration_in_seconds": pipeline.get("duration_in_seconds")
})
return {
"success": True,
"pipelines": pipelines,
"total": data.get("size", len(pipelines)),
"page": data.get("page", page),
"pagelen": data.get("pagelen", pagelen)
}
elif response.status_code == 401:
return {"success": False, "error": "Authentication failed. Please reconfigure with setup_bitbucket."}
elif response.status_code == 404:
return {"success": False, "error": f"Repository '{ws}/{repo_slug}' not found or pipelines not enabled."}
else:
return {"success": False, "error": f"API error: {response.status_code} - {response.text}"}
except ValueError as e:
return {"success": False, "error": str(e)}
except httpx.RequestError as e:
return {"success": False, "error": f"Connection error: {str(e)}"}
except Exception as e:
return {"success": False, "error": f"Unexpected error: {str(e)}"}
@mcp.tool()
def get_pipeline(
repo_slug: str,
pipeline_uuid: str,
workspace: Optional[str] = None
) -> dict:
"""
Get details of a specific pipeline.
Args:
repo_slug: Repository slug (name)
pipeline_uuid: Pipeline UUID (with or without braces)
workspace: Bitbucket workspace (optional if configured)
Returns:
Pipeline details including state, steps, and duration
"""
try:
ws = get_workspace(workspace)
auth = get_auth()
if not pipeline_uuid.startswith("{"):
pipeline_uuid = f"{{{pipeline_uuid}}}"
with httpx.Client() as client:
response = client.get(
f"{BITBUCKET_API}/repositories/{ws}/{repo_slug}/pipelines/{pipeline_uuid}",
auth=auth,
timeout=30.0
)
if response.status_code == 200:
pipeline = response.json()
target = pipeline.get("target", {})
state = pipeline.get("state", {})
state_result = state.get("result", {})
return {
"success": True,
"uuid": pipeline.get("uuid"),
"build_number": pipeline.get("build_number"),
"state": state.get("name"),
"result": state_result.get("name") if state_result else None,
"branch": target.get("ref_name") or target.get("source"),
"commit_hash": target.get("commit", {}).get("hash") if target.get("commit") else None,
"created_on": pipeline.get("created_on"),
"completed_on": pipeline.get("completed_on"),
"duration_in_seconds": pipeline.get("duration_in_seconds"),
"has_variables": pipeline.get("has_variables", False)
}
elif response.status_code == 401:
return {"success": False, "error": "Authentication failed. Please reconfigure with setup_bitbucket."}
elif response.status_code == 404:
return {"success": False, "error": f"Pipeline '{pipeline_uuid}' not found in '{ws}/{repo_slug}'."}
else:
return {"success": False, "error": f"API error: {response.status_code} - {response.text}"}
except ValueError as e:
return {"success": False, "error": str(e)}
except httpx.RequestError as e:
return {"success": False, "error": f"Connection error: {str(e)}"}
except Exception as e:
return {"success": False, "error": f"Unexpected error: {str(e)}"}
@mcp.tool()
def trigger_pipeline(
repo_slug: str,
branch: Optional[str] = None,
custom_pipeline: Optional[str] = None,
variables: Optional[list[dict]] = None,
workspace: Optional[str] = None
) -> dict:
"""
Trigger a new pipeline run.
Args:
repo_slug: Repository slug (name)
branch: Branch to run pipeline on (optional, defaults to main branch)
custom_pipeline: Name of custom pipeline to run (optional)
variables: List of pipeline variables as [{"key": "name", "value": "val", "secured": false}] (optional)
workspace: Bitbucket workspace (optional if configured)
Returns:
Triggered pipeline details or error message
"""
try:
ws = get_workspace(workspace)
auth = get_auth()
target = {
"type": "pipeline_ref_target",
"ref_type": "branch",
"ref_name": branch or "main",
}
if custom_pipeline:
target["selector"] = {
"type": "custom",
"pattern": custom_pipeline
}
payload = {"target": target}
if variables:
payload["variables"] = variables
with httpx.Client() as client:
response = client.post(
f"{BITBUCKET_API}/repositories/{ws}/{repo_slug}/pipelines",
auth=auth,
json=payload,
timeout=30.0
)
if response.status_code in (200, 201):
pipeline = response.json()
state = pipeline.get("state", {})
return {
"success": True,
"uuid": pipeline.get("uuid"),
"build_number": pipeline.get("build_number"),
"state": state.get("name"),
"created_on": pipeline.get("created_on"),
"message": f"Pipeline triggered on branch '{branch or 'main'}'"
}
elif response.status_code == 400:
error_data = response.json()
error_msg = error_data.get("error", {}).get("message", "Bad request")
return {"success": False, "error": error_msg}
elif response.status_code == 401:
return {"success": False, "error": "Authentication failed. Please reconfigure with setup_bitbucket."}
elif response.status_code == 404:
return {"success": False, "error": f"Repository '{ws}/{repo_slug}' not found or pipelines not enabled."}
else:
return {"success": False, "error": f"API error: {response.status_code} - {response.text}"}
except ValueError as e:
return {"success": False, "error": str(e)}
except httpx.RequestError as e:
return {"success": False, "error": f"Connection error: {str(e)}"}
except Exception as e:
return {"success": False, "error": f"Unexpected error: {str(e)}"}
@mcp.tool()
def stop_pipeline(
repo_slug: str,
pipeline_uuid: str,
workspace: Optional[str] = None
) -> dict:
"""
Stop a running pipeline.
Args:
repo_slug: Repository slug (name)
pipeline_uuid: Pipeline UUID (with or without braces)
workspace: Bitbucket workspace (optional if configured)
Returns:
Confirmation or error message
"""
try:
ws = get_workspace(workspace)
auth = get_auth()
if not pipeline_uuid.startswith("{"):
pipeline_uuid = f"{{{pipeline_uuid}}}"
with httpx.Client() as client:
response = client.post(
f"{BITBUCKET_API}/repositories/{ws}/{repo_slug}/pipelines/{pipeline_uuid}/stopPipeline",
auth=auth,
timeout=30.0
)
if response.status_code in (200, 204):
return {
"success": True,
"message": f"Pipeline '{pipeline_uuid}' stop requested."
}
elif response.status_code == 401:
return {"success": False, "error": "Authentication failed. Please reconfigure with setup_bitbucket."}
elif response.status_code == 404:
return {"success": False, "error": f"Pipeline '{pipeline_uuid}' not found in '{ws}/{repo_slug}'."}
elif response.status_code == 409:
return {"success": False, "error": "Pipeline is not in a stoppable state."}
else:
return {"success": False, "error": f"API error: {response.status_code} - {response.text}"}
except ValueError as e:
return {"success": False, "error": str(e)}
except httpx.RequestError as e:
return {"success": False, "error": f"Connection error: {str(e)}"}
except Exception as e:
return {"success": False, "error": f"Unexpected error: {str(e)}"}
@mcp.tool()
def list_pipeline_steps(
repo_slug: str,
pipeline_uuid: str,
workspace: Optional[str] = None
) -> dict:
"""
List steps in a pipeline.
Args:
repo_slug: Repository slug (name)
pipeline_uuid: Pipeline UUID (with or without braces)
workspace: Bitbucket workspace (optional if configured)
Returns:
List of pipeline steps with their details
"""
try:
ws = get_workspace(workspace)
auth = get_auth()
if not pipeline_uuid.startswith("{"):
pipeline_uuid = f"{{{pipeline_uuid}}}"
with httpx.Client() as client:
response = client.get(
f"{BITBUCKET_API}/repositories/{ws}/{repo_slug}/pipelines/{pipeline_uuid}/steps",
auth=auth,
timeout=30.0
)
if response.status_code == 200:
data = response.json()
steps = []
for step in data.get("values", []):
state = step.get("state", {})
state_result = state.get("result", {})
steps.append({
"uuid": step.get("uuid"),
"name": step.get("name"),
"state": state.get("name"),
"result": state_result.get("name") if state_result else None,
"started_on": step.get("started_on"),
"completed_on": step.get("completed_on"),
"duration_in_seconds": step.get("duration_in_seconds"),
"image": step.get("image", {}).get("name") if step.get("image") else None
})
return {
"success": True,
"steps": steps,
"total": len(steps)
}
elif response.status_code == 401:
return {"success": False, "error": "Authentication failed. Please reconfigure with setup_bitbucket."}
elif response.status_code == 404:
return {"success": False, "error": f"Pipeline '{pipeline_uuid}' not found in '{ws}/{repo_slug}'."}
else:
return {"success": False, "error": f"API error: {response.status_code} - {response.text}"}
except ValueError as e:
return {"success": False, "error": str(e)}
except httpx.RequestError as e:
return {"success": False, "error": f"Connection error: {str(e)}"}
except Exception as e:
return {"success": False, "error": f"Unexpected error: {str(e)}"}
@mcp.tool()
def get_pipeline_step_log(
repo_slug: str,
pipeline_uuid: str,
step_uuid: str,
workspace: Optional[str] = None
) -> dict:
"""
Get the log output for a pipeline step.
Args:
repo_slug: Repository slug (name)
pipeline_uuid: Pipeline UUID (with or without braces)
step_uuid: Step UUID (with or without braces)
workspace: Bitbucket workspace (optional if configured)
Returns:
Step log content
"""
try:
ws = get_workspace(workspace)
auth = get_auth()
if not pipeline_uuid.startswith("{"):
pipeline_uuid = f"{{{pipeline_uuid}}}"
if not step_uuid.startswith("{"):
step_uuid = f"{{{step_uuid}}}"
with httpx.Client() as client:
response = client.get(
f"{BITBUCKET_API}/repositories/{ws}/{repo_slug}/pipelines/{pipeline_uuid}/steps/{step_uuid}/log",
auth=auth,
timeout=60.0
)
if response.status_code == 200:
log_content = response.text
max_size = 100 * 1024
truncated = False
if len(log_content) > max_size:
log_content = log_content[:max_size]
truncated = True
return {
"success": True,
"log": log_content,
"truncated": truncated,
"size_bytes": len(response.text)
}
elif response.status_code == 401:
return {"success": False, "error": "Authentication failed. Please reconfigure with setup_bitbucket."}
elif response.status_code == 404:
return {"success": False, "error": f"Pipeline step not found."}
else:
return {"success": False, "error": f"API error: {response.status_code} - {response.text}"}
except ValueError as e:
return {"success": False, "error": str(e)}
except httpx.RequestError as e:
return {"success": False, "error": f"Connection error: {str(e)}"}
except Exception as e:
return {"success": False, "error": f"Unexpected error: {str(e)}"}
@mcp.tool()
def list_pipeline_variables(
repo_slug: str,
workspace: Optional[str] = None,
page: int = 1,
pagelen: int = 25
) -> dict:
"""
List pipeline variables configured for a repository.
Args:
repo_slug: Repository slug (name)
workspace: Bitbucket workspace (optional if configured)
page: Page number for pagination (default: 1)
pagelen: Number of results per page, max 100 (default: 25)
Returns:
List of pipeline variables
"""
try:
ws = get_workspace(workspace)
auth = get_auth()
params = {
"page": page,
"pagelen": min(pagelen, 100)
}
with httpx.Client() as client:
response = client.get(
f"{BITBUCKET_API}/repositories/{ws}/{repo_slug}/pipelines_config/variables",
auth=auth,
params=params,
timeout=30.0
)
if response.status_code == 200:
data = response.json()
variables = []
for var in data.get("values", []):
variables.append({
"uuid": var.get("uuid"),
"key": var.get("key"),
"value": var.get("value"),
"secured": var.get("secured", False)
})
return {
"success": True,
"variables": variables,
"total": data.get("size", len(variables)),
"page": data.get("page", page),
"pagelen": data.get("pagelen", pagelen)
}
elif response.status_code == 401:
return {"success": False, "error": "Authentication failed. Please reconfigure with setup_bitbucket."}
elif response.status_code == 404:
return {"success": False, "error": f"Repository '{ws}/{repo_slug}' not found or pipelines not configured."}
else:
return {"success": False, "error": f"API error: {response.status_code} - {response.text}"}
except ValueError as e:
return {"success": False, "error": str(e)}
except httpx.RequestError as e:
return {"success": False, "error": f"Connection error: {str(e)}"}
except Exception as e:
return {"success": False, "error": f"Unexpected error: {str(e)}"}
@mcp.tool()
def create_pipeline_variable(
repo_slug: str,
key: str,
value: str,
secured: bool = False,
workspace: Optional[str] = None
) -> dict:
"""
Create a pipeline variable for a repository.
Args:
repo_slug: Repository slug (name)
key: Variable name
value: Variable value
secured: Whether the variable is secured/encrypted (default: False)
workspace: Bitbucket workspace (optional if configured)
Returns:
Created variable details or error message
"""
try:
ws = get_workspace(workspace)
auth = get_auth()
payload = {
"key": key,
"value": value,
"secured": secured
}
with httpx.Client() as client:
response = client.post(
f"{BITBUCKET_API}/repositories/{ws}/{repo_slug}/pipelines_config/variables",
auth=auth,
json=payload,
timeout=30.0
)
if response.status_code in (200, 201):
data = response.json()
return {
"success": True,
"uuid": data.get("uuid"),
"key": data.get("key"),
"secured": data.get("secured", False),
"message": f"Pipeline variable '{key}' created."
}
elif response.status_code == 401:
return {"success": False, "error": "Authentication failed. Please reconfigure with setup_bitbucket."}
elif response.status_code == 404:
return {"success": False, "error": f"Repository '{ws}/{repo_slug}' not found or pipelines not configured."}
elif response.status_code == 409:
return {"success": False, "error": f"Variable '{key}' already exists."}
else:
return {"success": False, "error": f"API error: {response.status_code} - {response.text}"}
except ValueError as e:
return {"success": False, "error": str(e)}
except httpx.RequestError as e:
return {"success": False, "error": f"Connection error: {str(e)}"}
except Exception as e:
return {"success": False, "error": f"Unexpected error: {str(e)}"}