Skip to main content
Glama
zalab-inc
by zalab-inc

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