Skip to main content
Glama
scoutos

Linear MCP Server

by scoutos

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
NameRequiredDescriptionDefault
assigneeIdNoThe ID of the user to assign the issue to
debugNoDebug mode to show extra diagnostics
descriptionNoThe detailed description of the issue
priorityNoThe priority of the issue (0-4)
projectIdNoThe ID of the project to associate with the issue
stateIdNoThe ID of the state to set for the issue
teamIdYesThe ID of the Linear team where the issue will be created
titleYesThe title of the issue to create

Implementation Reference

  • 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,
        };
      }
    };
  • 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'),
    });
  • 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,
    });
  • 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),
    ];
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries the full burden of behavioral disclosure. It states 'Create a new issue,' which implies a write operation, but doesn't disclose permissions required, whether it's idempotent, rate limits, or what happens on failure. For a mutation tool with zero annotation coverage, this is a significant gap in behavioral context.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is two sentences, front-loaded with the core purpose and followed by a usage hint. It avoids redundancy and wastes no words, though it could be slightly more structured by explicitly separating purpose from guidelines.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the complexity (8 parameters, 2 required), no annotations, and no output schema, the description is moderately complete. It covers the basic purpose but lacks details on behavioral traits, error handling, and output format. For a creation tool with no structured safety or output information, it should do more to compensate, but it's not entirely inadequate.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the schema already documents all 8 parameters with clear descriptions. The description adds no parameter-specific information beyond the general purpose. According to the rules, when schema coverage is high (>80%), the baseline is 3 even with no param info in the description, which applies here.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the action ('Create a new issue') and resource ('in Linear'), with specific examples of what can be created ('tasks, bugs, or feature requests'). It distinguishes from siblings like 'add_comment' or 'get_issue' by focusing on creation rather than modification or retrieval. However, it doesn't explicitly differentiate from other creation-related tools if they existed.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description implies usage by stating 'This tool is useful for adding new tasks, bugs, or feature requests,' which suggests when to use it. However, it doesn't provide explicit guidance on when to choose this over alternatives like 'list_issues' for viewing or 'add_comment' for updating, nor does it mention prerequisites or exclusions.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Related Tools

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/scoutos/mcp-linear'

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