create_issue
Add tasks, bugs, or feature requests to your Linear workspace with clarity and organization by specifying title, team, description, priority, assignee, and project details.
Instructions
Create a new issue in Linear. This tool is useful for adding new tasks, bugs, or feature requests to your Linear workspace.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| assigneeId | No | The ID of the user to assign the issue to | |
| debug | No | Debug mode to show extra diagnostics | |
| description | No | The detailed description of the issue | |
| priority | No | The priority of the issue (0-4) | |
| projectId | No | The ID of the project to associate with the issue | |
| stateId | No | The ID of the state to set for the issue | |
| teamId | Yes | The ID of the Linear team where the issue will be created | |
| title | Yes | The title of the issue to create |
Implementation Reference
- src/tools/create-issue.js:185-362 (handler)Main handler function for the 'create_issue' tool. Handles input, creates Linear client, calls the createIssue helper, formats success response with issue details, and provides detailed error messages with optional debug info.const handler = async ( ctx, { title, teamId, description, priority, assigneeId, stateId, projectId, debug, } ) => { const logger = ctx.effects.logger; try { // Log details about config and parameters logger.debug('Create issue called with parameters:', { title, teamId, description: description ? `${description.substring(0, 20)}...` : undefined, priority, assigneeId, stateId, projectId, debug, }); // Debug log for API key (masked) const apiKey = ctx.config.linearApiKey || ''; const maskedKey = apiKey ? apiKey.substring(0, 4) + '...' + apiKey.substring(apiKey.length - 4) : '<not set>'; logger.debug(`Using Linear API key: ${maskedKey}`); if (!ctx.config.linearApiKey) { throw new Error('LINEAR_API_KEY is not configured'); } // Create a Linear client using our effect logger.debug('Creating Linear client'); const linearClient = ctx.effects.linear.createClient( ctx.config.linearApiKey ); // Create the issue using the Linear SDK client logger.debug('Executing Linear API to create issue'); const options = { description, priority, assigneeId, stateId, projectId, }; const result = await createIssue( linearClient, title, teamId, options, logger ); // Log that we created the issue logger.info(`Created issue with ID: ${result.id}`); // Format the output const formatDate = timestamp => { if (!timestamp) return 'Just now'; const date = new Date(timestamp); return date.toLocaleString(); }; let responseText = ''; responseText += `✅ Issue created successfully\n\n`; // Add issue details responseText += `**Issue ID:** ${result.id}\n`; responseText += `**Title:** ${result.title}\n`; if (result.status) { responseText += `**Status:** ${result.status}\n`; } if (result.priority !== undefined) { const priorityLabels = ['No priority', 'Urgent', 'High', 'Medium', 'Low']; responseText += `**Priority:** ${priorityLabels[result.priority]}\n`; } if (result.assignee) { responseText += `**Assigned to:** ${result.assignee.name}\n`; } if (result.project) { responseText += `**Project:** ${result.project.name}\n`; } responseText += `**Created at:** ${formatDate(result.createdAt)}\n\n`; if (result.description) { responseText += `**Description:**\n${result.description}\n`; } logger.debug('Returning formatted issue result'); return { content: [{ type: 'text', text: responseText }], }; } catch (error) { logger.error(`Error creating issue: ${error.message}`); logger.error(error.stack); // Create a user-friendly error message with troubleshooting guidance let errorMessage = `Error creating issue: ${error.message}`; // Add detailed diagnostic information if in debug mode if (debug) { errorMessage += '\n\n=== DETAILED DEBUG INFORMATION ==='; // Add parameters that were used errorMessage += `\nParameters: - title: ${title} - teamId: ${teamId} - description: ${description ? 'provided' : 'not provided'} - priority: ${priority !== undefined ? priority : 'not provided'} - assigneeId: ${assigneeId || 'not provided'} - stateId: ${stateId || 'not provided'} - projectId: ${projectId || 'not provided'}`; // Check if API key is configured const apiKey = ctx.config.linearApiKey || ''; const keyStatus = apiKey ? `API key is configured (${apiKey.substring( 0, 4 )}...${apiKey.substring(apiKey.length - 4)})` : 'API key is NOT configured - set LINEAR_API_KEY'; errorMessage += `\n\nLinear API Status: ${keyStatus}`; // Add error details if (error.name) { errorMessage += `\nError type: ${error.name}`; } if (error.code) { errorMessage += `\nError code: ${error.code}`; } if (error.stack) { errorMessage += `\n\nStack trace: ${error.stack .split('\n') .slice(0, 3) .join('\n')}`; } // Add Linear API info for manual testing errorMessage += `\n\nLinear API: Using official Linear SDK (@linear/sdk) For manual testing, try using the SDK directly or the Linear API Explorer in the Linear UI.`; } // Add a note that debug mode can be enabled for more details if (!debug) { errorMessage += `\n\nFor more detailed diagnostics, retry with debug:true in the input.`; } return { content: [ { type: 'text', text: errorMessage, }, ], isError: true, }; } };
- src/tools/create-issue.js:22-54 (schema)Zod input schema defining parameters for the create_issue tool, including title, teamId (required), and optional fields like description, priority, assignee, etc.const CreateIssueInputSchema = z.object({ title: z.string().describe('The title of the issue to create'), teamId: z .string() .min(1, { message: 'Team ID is required' }) .describe('The ID of the Linear team where the issue will be created'), description: z .string() .optional() .describe('The detailed description of the issue'), priority: z .number() .min(0) .max(4) .optional() .describe('The priority of the issue (0-4)'), assigneeId: z .string() .optional() .describe('The ID of the user to assign the issue to'), stateId: z .string() .optional() .describe('The ID of the state to set for the issue'), projectId: z .string() .optional() .describe('The ID of the project to associate with the issue'), debug: z .boolean() .default(false) .describe('Debug mode to show extra diagnostics'), });
- src/tools/create-issue.js:367-373 (registration)Tool factory using create_tool() that registers the 'create_issue' tool with its name, description, input schema, and handler function.export const CreateIssue = create_tool({ name: 'create_issue', description: 'Create a new issue in Linear. This tool is useful for adding new tasks, bugs, or feature requests to your Linear workspace.', inputSchema: CreateIssueInputSchema, handler, });
- src/tools/create-issue.js:71-179 (helper)Helper function that performs the actual Linear SDK issue creation, input preparation, response processing with schema validation, and async data loading.async function createIssue(client, title, teamId, options = {}, logger) { try { logger?.debug(`Creating new Linear issue with title: ${title}`); // Prepare the issue creation input for the Linear SDK const issueInput = { title, teamId, description: options.description, priority: options.priority, assigneeId: options.assigneeId, stateId: options.stateId, projectId: options.projectId, }; // Filter out undefined values Object.keys(issueInput).forEach(key => { if (issueInput[key] === undefined) { delete issueInput[key]; } }); logger?.debug('Issue creation payload:', issueInput); // Create the issue using the Linear SDK const issueResult = await client.createIssue(issueInput); if (!issueResult) { throw new Error('Failed to create issue, received null response'); } // Linear API returns a promise for the created issue const issueData = await issueResult.issue; if (!issueData) { throw new Error('Failed to retrieve issue data from response'); } logger?.debug(`Successfully created issue: ${issueData.id}`); // Load related data asynchronously let assigneeData = null; let projectData = null; let stateData = null; if (issueData.assignee) { assigneeData = await issueData.assignee; } if (issueData.project) { projectData = await issueData.project; } if (issueData.state) { stateData = await issueData.state; } // Process and validate the issue data using our schema const processedIssue = IssueSchema.parse({ id: issueData.id, title: issueData.title, description: issueData.description || undefined, // Convert null to undefined status: stateData?.name, priority: issueData.priority, assignee: assigneeData ? { id: assigneeData.id, name: assigneeData.name, email: assigneeData.email, } : undefined, project: projectData ? { id: projectData.id, name: projectData.name, } : undefined, createdAt: issueData.createdAt instanceof Date ? issueData.createdAt.toISOString() : issueData.createdAt, updatedAt: issueData.updatedAt instanceof Date ? issueData.updatedAt.toISOString() : issueData.updatedAt, }); return processedIssue; } catch (error) { // Enhanced error logging logger?.error(`Error creating Linear issue: ${error.message}`, { title, teamId, options, stack: error.stack, }); // Check if it's a Zod validation error (formatted differently) if (error.name === 'ZodError') { logger?.error( 'Zod validation error details:', JSON.stringify(error.errors, null, 2) ); } // Rethrow the error for the tool to handle throw error; } }
- src/index.js:109-118 (registration)Instantiation of the CreateIssue tool class with toolContext and addition to the all_tools array for MCP server registration.const all_tools = [ new tools.ListIssues(toolContext), new tools.GetIssue(toolContext), new tools.ListMembers(toolContext), new tools.ListProjects(toolContext), new tools.GetProject(toolContext), new tools.ListTeams(toolContext), new tools.AddComment(toolContext), new tools.CreateIssue(toolContext), ];