Skip to main content
Glama
update_plane_issue_v2.py8.07 kB
import json import requests import os import re def get_plane_issue_id(params_json: str = "{}") -> str: """ Get the UUID of a Plane issue using its display code (e.g. 'CLT-37'). Args: params_json: A JSON string containing: { "issue_code": "PROJECT_CODE-NUMBER" # e.g. "CLT-37" } Returns: str: Issue UUID if found, or error message Example: >>> get_plane_issue_id('{"issue_code": "CLT-37"}') "550e8400-e29b-41d4-a716-446655440000" """ try: # Parse parameters try: params = json.loads(params_json) if params_json else {} except json.JSONDecodeError: return "Error: Invalid JSON parameters" # Validate parameters if not params.get("issue_code"): return "Error: Issue code is required" issue_code = params["issue_code"] # Parse issue code format (e.g. "CLT-37") match = re.match(r"([A-Za-z]+)-(\d+)", issue_code) if not match: return "Error: Invalid issue code format. Expected format: PROJECT_CODE-NUMBER (e.g. CLT-37)" project_code = match.group(1) sequence_id = int(match.group(2)) # Configuration API_KEY = os.environ.get("PLANE_API_KEY", "plane_api_614f7240a5df4177840558c34bddb668") BASE_URL = os.environ.get("PLANE_BASE_URL", "http://192.168.50.90/api/v1") WORKSPACE_SLUG = os.environ.get("PLANE_WORKSPACE_SLUG", "test-space") headers = { "X-API-Key": API_KEY, "Content-Type": "application/json" } # First get projects to find the one matching the code projects_url = f"{BASE_URL}/workspaces/{WORKSPACE_SLUG}/projects" projects_response = requests.get(projects_url, headers=headers) if projects_response.status_code != 200: return f"Error: Failed to get projects - {projects_response.status_code}" projects = projects_response.json() project = None # Find project with matching identifier for p in projects: if p.get("identifier", "").upper() == project_code.upper(): project = p break if not project: return f"Error: No project found with identifier {project_code}" # Get issues for the project issues_url = f"{BASE_URL}/workspaces/{WORKSPACE_SLUG}/projects/{project['id']}/issues/" issues_response = requests.get(issues_url, headers=headers) if issues_response.status_code != 200: return f"Error: Failed to get issues - {issues_response.status_code}" issues = issues_response.json() # Find issue with matching sequence_id for issue in issues: if issue.get("sequence_id") == sequence_id: return json.dumps({ "issue_id": issue["id"], "project_id": project["id"], "name": issue["name"], "current_state": issue.get("state") }) return f"Error: No issue found with sequence ID {sequence_id} in project {project_code}" except requests.RequestException as e: return f"Error: Network error - {str(e)}" except Exception as e: return f"Error: Unexpected error - {str(e)}" def update_plane_issue(params_json: str = "{}") -> str: """ Update an existing issue in a Plane project. Args: params_json: A JSON string containing update details: { "project_id": "uuid-of-project", "issue_id": "uuid-of-issue", "state_id": "uuid-of-state", # Optional "name": "New title", # Optional "description": "New description", # Optional "priority": "none|low|medium|high|urgent", # Optional "assignee_ids": ["user-uuid"], # Optional "label_ids": ["label-uuid"], # Optional "start_date": "YYYY-MM-DD", # Optional "target_date": "YYYY-MM-DD" # Optional } Returns: str: Success message with updated issue details or error message Example: >>> update_plane_issue('{"project_id": "123", "issue_id": "456", "state_id": "789"}') "Issue updated: Bug Fix (#123) - Status changed" """ try: # Parse parameters try: params = json.loads(params_json) if params_json else {} except json.JSONDecodeError: return "Error: Invalid JSON parameters" # Validate required parameters if not params.get("project_id"): return "Error: Project ID is required" if not params.get("issue_id"): return "Error: Issue ID is required" # Check if issue_id is a UUID, if not, try to resolve it issue_id = params["issue_id"] if not re.match(r"^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$", issue_id): # Try to resolve the issue ID issue_resolution_params = json.dumps({"issue_code": issue_id}) resolution_result = get_plane_issue_id(issue_resolution_params) try: resolution_data = json.loads(resolution_result) if "issue_id" in resolution_data: params["issue_id"] = resolution_data["issue_id"] params["project_id"] = resolution_data["project_id"] else: return f"Error: Could not resolve issue code {issue_id} to a UUID. Details: {resolution_result}" except json.JSONDecodeError: return f"Error: Could not resolve issue code {issue_id} to a UUID. Details: {resolution_result}" # Configuration API_KEY = os.environ.get("PLANE_API_KEY", "plane_api_614f7240a5df4177840558c34bddb668") BASE_URL = os.environ.get("PLANE_BASE_URL", "http://192.168.50.90/api/v1") WORKSPACE_SLUG = os.environ.get("PLANE_WORKSPACE_SLUG", "test-space") headers = { "X-API-Key": API_KEY, "Content-Type": "application/json" } # Remove required fields from update data update_data = params.copy() update_data.pop("project_id") update_data.pop("issue_id") # If description is updated, also update HTML version if "description" in update_data: update_data["description_html"] = f"<p>{update_data['description']}</p>" if not update_data: return "Error: No update parameters provided" url = f"{BASE_URL}/workspaces/{WORKSPACE_SLUG}/projects/{params['project_id']}/issues/{params['issue_id']}/" response = requests.patch(url, headers=headers, json=update_data) if response.status_code == 200: result = response.json() changes = [] if "state_id" in update_data: changes.append("status changed") if "name" in update_data: changes.append("title updated") if "description" in update_data: changes.append("description updated") if "priority" in update_data: changes.append(f"priority set to {update_data['priority']}") if "assignee_ids" in update_data: changes.append("assignees updated") change_text = " - " + ", ".join(changes) if changes else "" return f"Issue updated: {result['name']} (#{result['sequence_id']}){change_text}" return f"Error: API request failed with status code {response.status_code}" except requests.RequestException as e: return f"Error: Network error - {str(e)}" except Exception as e: return f"Error: Unexpected error - {str(e)}"

Latest Blog Posts

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/oculairmedia/plane-projectmanagement_mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server