update-issue
Modify existing project issues by updating titles, descriptions, priorities, states, or assignees to keep project tracking current and accurate.
Instructions
Update an existing issue in a project, delete just update the issue title with 'delete' or 'remove'
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| assignees | No | Updated array of user IDs to assign to this issue (optional) | |
| description_html | No | HTML description of the issue (required by Plane API) | |
| issue_id | Yes | ID of the issue to update | |
| name | No | Updated title of the issue (optional) | |
| priority | No | Updated priority of the issue (optional) | |
| project_id | Yes | ID of the project containing the issue | |
| state_id | No | Updated state ID of the issue (optional) |
Implementation Reference
- src/index.ts:386-432 (handler)Handles execution of the 'update-issue' tool: validates project_id and issue_id, normalizes assignees array, calls Plane API PATCH endpoint, returns updated issue JSON.case "update-issue": { if ( !args || typeof args.project_id !== "string" || typeof args.issue_id !== "string" ) { throw new Error("Project ID and Issue ID are required"); } const { project_id, issue_id, ...updateData } = args; // Ensure assignees is properly formatted as an array if (updateData.assignees) { // Special case: detect if entire issue is nested in assignees if ( typeof updateData.assignees === "object" && !Array.isArray(updateData.assignees) && (updateData.assignees as Record<string, any>).project_id && (updateData.assignees as Record<string, any>).name ) { // Issue is nested inside assignees, remove it completely delete updateData.assignees; } else if (!Array.isArray(updateData.assignees)) { if (typeof updateData.assignees === "string") { // Convert single string to array updateData.assignees = [updateData.assignees]; } else if (typeof updateData.assignees === "object") { // Convert object to array of values updateData.assignees = Object.values(updateData.assignees); } else { // Remove invalid assignees delete updateData.assignees; } } } const updatedIssue = await callPlaneAPI( `/projects/${project_id}/issues/${issue_id}/`, "PATCH", updateData ); return { content: [ { type: "text", text: JSON.stringify(updatedIssue, null, 2) }, ], isError: false, }; }
- src/index.ts:145-188 (schema)Defines the Tool object for 'update-issue' including name, description, and detailed inputSchema with properties and requirements.const UPDATE_ISSUE_TOOL: Tool = { name: "update-issue", description: "Update an existing issue in a project, delete just update the issue title with 'delete' or 'remove'", inputSchema: { type: "object", properties: { project_id: { type: "string", description: "ID of the project containing the issue", }, issue_id: { type: "string", description: "ID of the issue to update", }, name: { type: "string", description: "Updated title of the issue (optional)", }, description_html: { type: "string", description: "HTML description of the issue (required by Plane API)", }, priority: { type: "string", description: "Updated priority of the issue (optional)", enum: ["urgent", "high", "medium", "low", "none"], }, state_id: { type: "string", description: "Updated state ID of the issue (optional)", }, assignees: { type: "array", items: { type: "string", }, description: "Updated array of user IDs to assign to this issue (optional)", }, }, required: ["project_id", "issue_id"], }, };
- src/index.ts:261-270 (registration)Registers the 'update-issue' tool (UPDATE_ISSUE_TOOL) in the list of available tools returned by the ListToolsRequestSchema handler.server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: [ LIST_PROJECTS_TOOL, GET_PROJECT_TOOL, CREATE_ISSUE_TOOL, LIST_ISSUES_TOOL, GET_ISSUE_TOOL, UPDATE_ISSUE_TOOL, ], }));
- src/index.ts:197-245 (helper)Shared utility function for making authenticated API calls to Plane.so, used by the update-issue handler for the PATCH request.async function callPlaneAPI( endpoint: string, method: string, body?: any ): Promise<any> { const baseUrl = `https://api.plane.so/api/v1/workspaces/${PLANE_WORKSPACE_SLUG}`; const url = `${baseUrl}${endpoint}`; const options: RequestInit = { method, headers: { "Content-Type": "application/json", "X-API-Key": PLANE_API_KEY as string, }, }; if (body && (method === "POST" || method === "PATCH")) { options.body = JSON.stringify(body); } try { const response = await fetch(url, options); if (!response.ok) { let errorText; try { errorText = await response.text(); } catch (parseError) { errorText = "Unable to parse error response"; } throw new Error( `Plane API error: ${response.status} ${response.statusText}\n${errorText}` ); } // For DELETE requests that return 204 No Content if (response.status === 204) { return { success: true }; } return await response.json(); } catch (error) { throw new Error( `Error calling Plane API: ${ error instanceof Error ? error.message : String(error) }` ); } }