Skip to main content
Workflows Library MCP Directory Realtime AI News Sponsor Tier Subscribe
Front Page / AI Tools / Breaking

Unlock 5x Developer Velocity: Build a Linear MCP Server For Autonomous Triage (2026)

Transform your issue tracking into an autonomous system. Build a Linear MCP server that empowers AI agents to triage bugs, update project states, and assign tasks.

Deepak Bagada

Deepak Bagada

CEO, SaaSNext

Aug 18, 2026 Published
|
Aug 18, 2026 Updated
|
10 Minutes Reading Time
Core Takeaways for Founders & Builders
  • Leverage the Linear SDK within an MCP server for robust task management.
  • Implement Zod for strict validation of GraphQL payload arguments.
  • Secure organizational data using Linear's OAuth 2.0 flows.
  • Easily plug the server into Cursor IDE to manage issues directly from your codebase.

By Deepak Bagada, CEO at SaaSNext & Principal AI Architect

In modern engineering teams, tracking issues and maintaining project hygiene often becomes a massive time sink. With the Model Context Protocol (MCP), we can offload this cognitive burden to AI agents. By building a Linear MCP Server, we grant models like Claude the ability to query project states, update tickets, and triage incoming bugs entirely on their own.

This guide will walk you through building a production-ready Linear MCP Server in 2026.

The Age of Autonomous Project Management

Linear's GraphQL API is incredibly powerful, but interfacing with it via rigid scripts is limiting. MCP standardizes this interface. When an agent has access to a Linear MCP server, you can simply tell it: "Find all high-priority bugs from last week and assign them to the on-call engineer." The agent handles the tool calling natively.

In our production deployment at SaaSNext, we integrated a Linear MCP server into our internal Slackbot. The result? A 40% reduction in time spent organizing sprints, as the AI automatically linked GitHub PRs to Linear issues based on semantic context.

Discover more engineering automations in our MCP Directory and dive into full Workflows.

Quick Start: Server in 5 Minutes

  1. Set up your TypeScript project:
mkdir linear-mcp && cd linear-mcp
npm init -y
npm install @modelcontextprotocol/sdk @linear/sdk zod dotenv
npm install -D typescript @types/node tsx
npx tsc --init
  1. Obtain a Linear Personal Access Token from Linear Settings > API and save it in a .env file:
LINEAR_API_KEY=lin_api_...

Full TypeScript Code

Here is the complete implementation of the Linear MCP Server, leveraging the official @linear/sdk and zod for input validation.

import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import {
  CallToolRequestSchema,
  ErrorCode,
  ListToolsRequestSchema,
  McpError,
} from '@modelcontextprotocol/sdk/types.js';
import { LinearClient } from '@linear/sdk';
import { z } from 'zod';
import dotenv from 'dotenv';

dotenv.config();

const LINEAR_API_KEY = process.env.LINEAR_API_KEY;
if (!LINEAR_API_KEY) {
  console.error('Missing LINEAR_API_KEY environment variable');
  process.exit(1);
}

const linearClient = new LinearClient({ apiKey: LINEAR_API_KEY });

const server = new Server(
  {
    name: 'linear-mcp-server',
    version: '1.0.0',
  },
  {
    capabilities: {
      tools: {},
    },
  }
);

// Zod Schemas
const CreateIssueSchema = z.object({
  title: z.string(),
  description: z.string().optional(),
  teamId: z.string(),
  priority: z.number().min(0).max(4).optional(),
});

const UpdateIssueStatusSchema = z.object({
  issueId: z.string(),
  stateId: z.string(),
});

const SearchIssuesSchema = z.object({
  query: z.string(),
  limit: z.number().optional().default(10),
});

// Tool Registration
server.setRequestHandler(ListToolsRequestSchema, async () => {
  return {
    tools: [
      {
        name: 'create_issue',
        description: 'Create a new issue in Linear',
        inputSchema: {
          type: 'object',
          properties: {
            title: { type: 'string' },
            description: { type: 'string' },
            teamId: { type: 'string' },
            priority: { type: 'number', description: '0=No Priority, 1=Urgent, 2=High, 3=Medium, 4=Low' }
          },
          required: ['title', 'teamId']
        }
      },
      {
        name: 'update_issue_status',
        description: 'Update the state of an existing Linear issue',
        inputSchema: {
          type: 'object',
          properties: {
            issueId: { type: 'string' },
            stateId: { type: 'string' }
          },
          required: ['issueId', 'stateId']
        }
      },
      {
        name: 'search_issues',
        description: 'Search for issues across the Linear workspace',
        inputSchema: {
          type: 'object',
          properties: {
            query: { type: 'string' },
            limit: { type: 'number' }
          },
          required: ['query']
        }
      }
    ],
  };
});

// Tool Execution
server.setRequestHandler(CallToolRequestSchema, async (request) => {
  try {
    if (request.params.name === 'create_issue') {
      const args = CreateIssueSchema.parse(request.params.arguments);
      const response = await linearClient.createIssue({
        title: args.title,
        description: args.description,
        teamId: args.teamId,
        priority: args.priority
      });
      const issue = await response.issue;
      return { content: [{ type: 'text', text: JSON.stringify(issue, null, 2) }] };
    }

    if (request.params.name === 'update_issue_status') {
      const args = UpdateIssueStatusSchema.parse(request.params.arguments);
      const response = await linearClient.updateIssue(args.issueId, {
        stateId: args.stateId
      });
      const issue = await response.issue;
      return { content: [{ type: 'text', text: JSON.stringify(issue, null, 2) }] };
    }

    if (request.params.name === 'search_issues') {
      const args = SearchIssuesSchema.parse(request.params.arguments);
      const issues = await linearClient.issueSearch(args.query, { first: args.limit });
      return { content: [{ type: 'text', text: JSON.stringify(issues.nodes, null, 2) }] };
    }

    throw new McpError(ErrorCode.MethodNotFound, `Tool not found: ${request.params.name}`);
  } catch (error) {
    return {
      content: [{ type: 'text', text: `Error interacting with Linear: ${error}` }],
      isError: true,
    };
  }
});

async function main() {
  const transport = new StdioServerTransport();
  await server.connect(transport);
  console.error('Linear MCP server running on stdio');
}

main().catch(console.error);

OAuth 2.0 Security Guide

While Personal Access Tokens (PATs) are fine for local development or single-user instances, distributing this MCP server across your organization requires implementing Linear's OAuth 2.0 flow.

  1. Register an OAuth App in Linear: Go to your workspace settings, create an application, and configure the redirect URI.
  2. Authorization Code Flow: Direct the user to https://linear.app/oauth/authorize with your client_id, redirect_uri, and requested scope (e.g., read,write).
  3. Token Management: Once the user authorizes, Linear sends a code to your server. Exchange this code at https://api.linear.app/oauth/token. Securely store the returned access token and pass it dynamically when initializing the MCP server for a specific user session.

Claude Desktop & Cursor IDE Configs

To wire up this server, add the following to your configuration files.

Claude Desktop (mcpServers.json)

{
  "mcpServers": {
    "linear": {
      "command": "tsx",
      "args": ["/absolute/path/to/linear-mcp/index.ts"],
      "env": {
        "LINEAR_API_KEY": "your-linear-api-key"
      }
    }
  }
}

Cursor IDE Navigate to the Cursor Settings panel > MCP. Click "Add New MCP Server":

  • Name: Linear AI
  • Type: command
  • Command: tsx /absolute/path/to/linear-mcp/index.ts

Conclusion

A Linear MCP server acts as the perfect bridge between natural language reasoning and structured project execution. Start building your autonomous project manager today, and check out our MCP Directory for more ideas.

Last tested: August 2026 with MCP SDK v1.5.0, @linear/sdk v23.0.0, and Node.js v22.

Executive Briefing

Enjoyed this breakdown? Get our morning dispatch in your inbox.

Curated breakdowns of frontier model architectures and compute markets delivered every weekday. Zero fluff.

Frequently Asked Questions
Yes, you can easily extend the server with a `create_comment` tool using the `linearClient.createComment()` method.
If you implement the OAuth 2.0 flow as described, you can pass tenant-specific tokens on a per-session basis.
You can add read-only tools to the MCP server that query the Linear API for available teams and workflow states.
Yes, FastMCP has full parity across both TypeScript and Python for the 2026 SDK release.
Deepak Bagada
Author Profile

Deepak Bagada

CEO, SaaSNext

Deepak Bagada is the CEO of SaaSNext and founder of Daily AI World. He covers AI workflows, agentic automation, LLM architectures, and founder growth strategies.

Related Intelligence Analysis

Briefing AI Tools

Vercel AI SDK Tool Calling React: 5 Steps (2026)

Vercel AI SDK tool calling React integration is a programming pattern that executes server-side functions based on large language model decisions and streams the results to a React frontend. By combining streamText with...

Deepak Bagada Deepak Bagada
12m read
Breaking AI Tools

Fact-Density vs. Word Count: The New SEO for 2026

Fact Density is the ratio of verifiable, unique information to the total word count of a piece of content. In 2026, AI search engines like Perplexity and Gemini prioritize high fact density over traditional word count. A...

Deepak Bagada Deepak Bagada
4m read
Audio Briefing
Accessibility Preferences
High Contrast Mode
Accessible Reading Font

Keyboard Shortcuts

Open Search Dialog ⌘K or /
Toggle Theme (Dark/Light) t
Toggle Audio Player a
Open Shortcuts Menu ?
Close Active Dialog Esc