Skip to main content
Glama

create_issue

Create a new issue in Linear with title, description, team assignment, due date, priority, status, and optional parent issue linkage for project tracking.

Instructions

A tool that creates an issue in Linear

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
descriptionYesThe description of the issue
dueDateNoThe due date of the issue
parentIdNoThe ID of the parent issue, used to create a sub-issue
priorityNoThe priority of the issueno_priority
statusNoThe status of the issuebacklog
teamIdYesThe team ID the issue belongs to
titleYesThe title of the issue

Implementation Reference

  • Full tool definition for 'create_issue' including name, schema, and complete handler logic that validates inputs, calls linearClient.createIssue, handles response, and formats output.
    export const LinearCreateIssueTool = createSafeTool({ name: "create_issue", description: "A tool that creates an issue in Linear", schema: createIssueSchema.shape, handler: async (args: z.infer<typeof createIssueSchema>) => { try { // Validate input if (!args.teamId || args.teamId.trim() === "") { return { content: [{ type: "text", text: "Error: Team ID cannot be empty", }], }; } if (!args.title || args.title.trim() === "") { return { content: [{ type: "text", text: "Error: Issue title 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 valid state ID from Linear API if status is provided let stateId: string | undefined; if (args.status) { // 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, args.teamId, linearClient); if (!stateId) { return { content: [{ type: "text", text: `Error: Could not find a valid state ID for "${args.status}" in team ${args.teamId}`, }], }; } } // Create the issue const createIssueResponse = await linearClient.createIssue({ title: args.title, description: args.description, stateId: stateId, dueDate: args.dueDate, priority: priorityValue, teamId: args.teamId, parentId: args.parentId, }); if (!createIssueResponse) { return { content: [{ type: "text", text: "Failed to create issue. Please check your parameters and try again.", }], }; } // Getting issue ID from response // Linear SDK returns results in success and entity pattern if (createIssueResponse.success) { // Access issue and get ID with correct data type const issue = await createIssueResponse.issue; if (issue && issue.id) { return { content: [{ type: "text", text: `Status: Success\nMessage: Linear issue created\nIssue ID: ${issue.id}`, }], }; } } // Extract data from response - fix to handle proper response structure const createResponse = createIssueResponse as unknown as LinearCreateResponse; // Check if the response follows the expected structure with success flag if (createResponse.success === false) { return { content: [{ type: "text", text: "Failed to create issue. Please check your parameters and try again.", }], }; } // Extract issue data from the correct property const issueData: IssueResponseData = createResponse.issue || createIssueResponse as unknown as IssueResponseData; // Directly check the parsed response result const issueId = issueData?.id || (createIssueResponse as unknown as { id?: string })?.id; if (issueId) { return { content: [{ type: "text", text: `Status: Success\nMessage: Linear issue created\nIssue ID: ${issueId}`, }], }; } if (!issueData) { // Display success message even if data is incomplete return { content: [{ type: "text", text: "Status: Success\nMessage: Linear issue created", }], }; } if (!issueData.id) { // Issue data exists but no ID return { content: [{ type: "text", text: "Status: Success\nMessage: Linear issue created (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 created\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 creating the issue:\n${errorMessage}`, }], }; } }, });
  • Zod schema definition for create_issue tool inputs.
    const createIssueSchema = z.object({ teamId: z.string().describe("The team ID the issue belongs to"), title: z.string().describe("The title of the issue"), description: z.string().describe("The description of the issue"), 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"), 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 LinearCreateIssueTool (create_issue) along with other tools in the MCP server main function.
    registerTool(server, [ LinearSearchIssuesTool, LinearGetProfileTool, LinearCreateIssueTool, LinearCreateCommentTool, LinearUpdateCommentTool, LinearGetIssueTool, LinearGetTeamIdTool, LinearUpdateIssueTool, LinearGetCommentTool, ]);
  • Helper function to format the created issue data into human-readable output used in the tool handler.
    function formatIssueToHumanReadable(issue: IssueResponseData): string { if (!issue || !issue.id) { return "Invalid or incomplete issue data"; } let result = "LINEAR ISSUE CREATED\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`; } // Dates result += `--- TIME INFO ---\n`; 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 created in Linear."; return result; }
  • Re-export of LinearCreateIssueTool from create-issue.js for use in 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