Skip to main content
Glama

update_issue

Modify Linear issue details including title, description, status, priority, due date, and parent relationships to keep project tracking current.

Instructions

A tool that updates an issue in Linear

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
descriptionNoThe description of the issue
dueDateNoThe due date of the issue
idYesThe ID of the issue to update
parentIdNoThe ID of the parent issue, used to create a sub-issue
priorityNoThe priority of the issueno_priority
sortOrderNoThe sort order of the issue
statusNoThe status of the issuebacklog
titleNoThe title of the issue
trashedNoWhether the issue is trashed

Implementation Reference

  • The handler function that executes the tool logic for updating a Linear issue. It validates inputs, resolves state IDs, calls the Linear API to update the issue, handles responses, and formats output.
    handler: async (args: z.infer<typeof updateIssueSchema>) => { try { // Validate input if (!args.id || args.id.trim() === "") { return { content: [{ type: "text", text: "Error: Issue ID cannot be empty", }], }; } // Convert priority from string to number if provided let priorityValue: number | undefined; if (args.priority) { priorityValue = PriorityStringToNumber[args.priority]; if (priorityValue === undefined) { return { content: [{ type: "text", text: "Error: Priority must be a valid string (no_priority, urgent, high, medium, low)", }], }; } } // Get the issue to update to retrieve its team ID let teamId: string | undefined; try { const issueResponse = await linearClient.issue(args.id); if (issueResponse) { const issueData = await issueResponse; const team = await issueData.team; if (team) { teamId = team.id; } } } catch (error) { console.error("Error fetching issue for team ID:", error); } // Get valid state ID from Linear API if status is provided let stateId: string | undefined; if (args.status && teamId) { // Normalize the state name to handle different variations const normalizedStateName = normalizeStateName(args.status); // Get the actual state ID from Linear API stateId = await getStateId(normalizedStateName, teamId, linearClient); if (!stateId) { return { content: [{ type: "text", text: `Error: Could not find a valid state ID for "${args.status}" in team of issue ${args.id}`, }], }; } } // Update the issue const updateIssueResponse = await linearClient.updateIssue(args.id, { title: args.title, description: args.description, trashed: args.trashed, dueDate: args.dueDate, sortOrder: args.sortOrder, stateId: stateId, priority: priorityValue, parentId: args.parentId, }); if (!updateIssueResponse) { return { content: [{ type: "text", text: "Failed to update issue. Please check your parameters and try again.", }], }; } // Get issue ID from response // Linear SDK returns results in success and entity pattern if (updateIssueResponse.success) { // Access issue and get ID with the correct data type const issue = await updateIssueResponse.issue; if (issue && issue.id) { return { content: [{ type: "text", text: `Status: Success\nMessage: Linear issue updated\nIssue ID: ${issue.id}`, }], }; } } // Extract data from response - fix to handle proper response structure const updateResponse = updateIssueResponse as unknown as LinearUpdateResponse; // Check if the response follows the expected structure with success flag if (updateResponse.success === false) { return { content: [{ type: "text", text: "Failed to update issue. Please check your parameters and try again.", }], }; } // Extract issue data from the correct property const issueData: IssueResponseData = updateResponse.issue || updateIssueResponse as unknown as IssueResponseData; // Directly check the parsed response result const issueId = issueData?.id || (updateIssueResponse as unknown as { id?: string })?.id; if (issueId) { return { content: [{ type: "text", text: `Status: Success\nMessage: Linear issue updated\nIssue ID: ${issueId}`, }], }; } if (!issueData) { // Display success message even if data is incomplete return { content: [{ type: "text", text: "Status: Success\nMessage: Linear issue updated", }], }; } if (!issueData.id) { // Issue data exists but no ID return { content: [{ type: "text", text: "Status: Success\nMessage: Linear issue updated (ID not available)", }], }; } // Success case with ID available if (issueData.title === undefined && issueData.description === undefined) { // Only ID is available, without complete data return { content: [{ type: "text", text: `Status: Success\nMessage: Linear issue updated\nIssue ID: ${issueData.id}`, }], }; } // Format issue data to human-readable text const formattedText = formatIssueToHumanReadable(issueData); // Return formatted text return { content: [{ type: "text", text: formattedText, }], }; } catch (error) { // Handle errors gracefully const errorMessage = error instanceof Error ? error.message : "Unknown error"; return { content: [{ type: "text", text: `An error occurred while updating the issue:\n${errorMessage}`, }], }; } },
  • Zod schema defining the input parameters and validation for the update_issue tool.
    const updateIssueSchema = z.object({ id: z.string().describe("The ID of the issue to update"), title: z.string().describe("The title of the issue").optional(), description: z.string().describe("The description of the issue").optional(), dueDate: z.string().describe("The due date of the issue").optional(), status: z.enum([ "triage", "backlog", "todo", "in_progress", "done", "canceled" ]).default("backlog").describe("The status of the issue"), priority: z.enum([ "no_priority", "urgent", "high", "medium", "low" ]).default("no_priority").describe("The priority of the issue"), sortOrder: z.number().describe("The sort order of the issue").optional(), trashed: z.boolean().describe("Whether the issue is trashed").optional(), parentId: z.string().describe("The ID of the parent issue, used to create a sub-issue").optional(), });
  • src/index.ts:31-41 (registration)
    Registration of the LinearUpdateIssueTool (internal name 'update_issue') along with other tools to the MCP server.
    registerTool(server, [ LinearSearchIssuesTool, LinearGetProfileTool, LinearCreateIssueTool, LinearCreateCommentTool, LinearUpdateCommentTool, LinearGetIssueTool, LinearGetTeamIdTool, LinearUpdateIssueTool, LinearGetCommentTool, ]);
  • Helper function used by the handler to format the updated issue data into a detailed human-readable string.
    function formatIssueToHumanReadable(issue: IssueResponseData): string { if (!issue || !issue.id) { return "Invalid or incomplete issue data"; } let result = "LINEAR ISSUE UPDATED\n"; result += "==================\n\n"; // Basic information result += `--- ISSUE DETAILS ---\n`; result += `ID: ${issue.id}\n`; result += `TITLE: ${safeText(issue.title)}\n`; result += `DESCRIPTION: ${safeText(issue.description)}\n\n`; // Status and priority result += `--- STATUS INFO ---\n`; if (issue.state && issue.state.name) { result += `STATUS: ${issue.state.name}\n`; } result += `PRIORITY: ${getPriorityLabel(issue.priority)}\n\n`; // Parent information if exists if (issue.parent && issue.parent.id) { result += `--- PARENT ISSUE ---\n`; result += `PARENT ID: ${issue.parent.id}\n`; if (issue.parent.title) { result += `PARENT TITLE: ${safeText(issue.parent.title)}\n`; } result += `\n`; } // Team information result += `--- TEAM INFO ---\n`; if (issue.team && issue.team.name) { result += `TEAM: ${issue.team.name}\n`; } // Assignee information if (issue.assignee && issue.assignee.name) { result += `ASSIGNEE: ${issue.assignee.name}\n`; } // Dates result += `--- TIME INFO ---\n`; if (issue.createdAt) { result += `CREATED AT: ${formatDate(issue.createdAt)}\n`; } result += `UPDATED AT: ${formatDate(issue.updatedAt)}\n`; // Due date if present if (issue.dueDate) { result += `DUE DATE: ${formatDate(issue.dueDate)}\n`; } // URL result += `\n--- ACCESS INFO ---\n`; result += `URL: ${safeText(issue.url)}\n\n`; result += "The issue has been successfully updated in Linear."; return result; }
  • Re-export of LinearUpdateIssueTool from its implementation file, facilitating import in src/index.ts.
    export { LinearGetProfileTool, LinearSearchIssuesTool, LinearCreateIssueTool, LinearUpdateIssueTool, LinearCreateCommentTool, LinearUpdateCommentTool, LinearGetIssueTool, LinearGetTeamIdTool, LinearGetCommentTool

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/zalab-inc/mcp-linear-app'

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