Skip to main content
Glama

Linear MCP Server

MCP server for Linear API. Streamlined issue tracking, project management, sprint planning, and team collaboration for modern development teams.

Features

  • Issue Management: Create, update, search, and track issues

  • Project Planning: Manage projects with milestones and roadmaps

  • Sprint Cycles: Track sprint progress and metrics

  • Team Collaboration: Organize teams and assign work

  • Labels & Organization: Custom labels and filtering

  • GraphQL API: Powerful, flexible queries

  • Real-time Updates: Live issue status tracking

  • Custom Fields: Priority levels, states, and metadata

  • Search & Filter: Advanced issue search capabilities

  • Roadmap Planning: Long-term project visibility

Setup

Prerequisites

  • Linear account (Free or paid plan)

  • API key with appropriate permissions

Environment Variables

  • LINEAR_API_KEY (required): Your Linear API key

How to get credentials:

  1. Go to linear.app/settings/api

  2. Sign in to your Linear workspace

  3. Click "Create new key" or "Personal API keys"

  4. Give your key a descriptive name

  5. Copy the generated key (starts with lin_api_)

  6. Store as LINEAR_API_KEY

API Key Format:

  • Format: lin_api_xxxxxxxxxxxxxxxxxxxxxxxx

  • Keep your key secure - it has full access to your workspace

Rate Limits

Standard Rate Limits:

  • 1500 requests per minute per IP address

  • 50 requests per second per IP address

  • Queries are counted by complexity (points system)

  • Simple queries: 1-5 points

  • Complex queries with relations: 10-50 points

Best Practices:

  • Batch operations when possible

  • Use pagination for large datasets

  • Cache frequently accessed data

  • Implement exponential backoff for retries

GraphQL API

Linear uses GraphQL, which means:

  • All requests POST to single endpoint: https://api.linear.app/graphql

  • Specify exactly what data you need

  • Combine multiple operations efficiently

  • Strong typing and introspection

  • Nested data fetching in single request

Available Tools

Issue Management

list_issues

List and filter issues across your workspace.

Parameters:

  • team_id (string, optional): Filter by team ID

  • project_id (string, optional): Filter by project ID

  • assignee_id (string, optional): Filter by assignee user ID

  • label_id (string, optional): Filter by label ID

  • state (string, optional): Filter by state name (backlog, unstarted, started, completed, canceled)

  • first (int, optional): Number of issues to return (default: 50)

Example:

# List all issues issues = await list_issues(first=20) # Filter by team issues = await list_issues(team_id="team-123", first=10) # Filter by state issues = await list_issues(state="started", first=25) # Multiple filters issues = await list_issues( team_id="team-123", assignee_id="user-456", state="unstarted" ) # Returns: # { # "data": { # "issues": { # "nodes": [ # { # "id": "issue-123", # "title": "Implement user authentication", # "description": "Add OAuth2 support...", # "priority": 2, # "state": { # "name": "In Progress", # "type": "started" # }, # "assignee": { # "id": "user-456", # "name": "Jane Smith" # }, # "labels": { # "nodes": [ # {"id": "label-789", "name": "backend"} # ] # }, # "createdAt": "2025-10-01T10:00:00Z", # "updatedAt": "2025-10-08T14:30:00Z" # } # ] # } # } # }

get_issue

Get detailed information about a specific issue.

Parameters:

  • issue_id (string, required): Issue ID

Example:

issue = await get_issue(issue_id="issue-123") # Returns: # { # "data": { # "issue": { # "id": "issue-123", # "title": "Fix login bug", # "description": "Users unable to login with email...", # "priority": 1, # "estimate": 3, # "state": { # "name": "In Progress", # "type": "started" # }, # "assignee": { # "id": "user-456", # "name": "Jane Smith", # "email": "jane@company.com" # }, # "labels": { # "nodes": [ # {"id": "label-123", "name": "bug", "color": "#ff0000"} # ] # }, # "project": { # "id": "proj-789", # "name": "Q4 Release" # }, # "team": { # "id": "team-123", # "name": "Engineering" # }, # "createdAt": "2025-10-05T09:00:00Z", # "updatedAt": "2025-10-08T15:00:00Z", # "url": "https://linear.app/company/issue/ENG-123" # } # } # }

create_issue

Create a new issue in Linear.

Parameters:

  • team_id (string, required): Team ID

  • title (string, required): Issue title

  • description (string, optional): Issue description (markdown supported)

  • priority (int, optional): Priority level (0=none, 1=urgent, 2=high, 3=medium, 4=low, default: 0)

  • assignee_id (string, optional): Assignee user ID

  • project_id (string, optional): Project ID

  • label_ids (list of strings, optional): List of label IDs

Priority Levels:

  • 0: No priority

  • 1: Urgent (red)

  • 2: High (orange)

  • 3: Medium (yellow)

  • 4: Low (blue)

Example:

# Simple issue issue = await create_issue( team_id="team-123", title="Add dark mode support" ) # Full issue with all fields issue = await create_issue( team_id="team-123", title="Implement user dashboard", description="Create a personalized dashboard with:\n- Activity feed\n- Quick actions\n- Stats overview", priority=2, assignee_id="user-456", project_id="proj-789", label_ids=["label-123", "label-456"] ) # Returns: # { # "data": { # "issueCreate": { # "success": true, # "issue": { # "id": "issue-new-123", # "title": "Implement user dashboard", # "url": "https://linear.app/company/issue/ENG-124" # } # } # } # }

update_issue

Update an existing issue.

Parameters:

  • issue_id (string, required): Issue ID

  • title (string, optional): Updated title

  • description (string, optional): Updated description

  • priority (int, optional): Updated priority (0-4)

  • state_id (string, optional): Updated state ID

  • assignee_id (string, optional): Updated assignee ID

Example:

# Update title result = await update_issue( issue_id="issue-123", title="Fix critical login bug" ) # Update multiple fields result = await update_issue( issue_id="issue-123", priority=1, assignee_id="user-789", description="Updated: Users getting 500 error on login" ) # Change state result = await update_issue( issue_id="issue-123", state_id="state-completed" ) # Returns: # { # "data": { # "issueUpdate": { # "success": true, # "issue": { # "id": "issue-123", # "title": "Fix critical login bug", # "state": { # "name": "Done" # } # } # } # } # }

delete_issue

Delete an issue.

Parameters:

  • issue_id (string, required): Issue ID

Example:

result = await delete_issue(issue_id="issue-123") # Returns: # { # "data": { # "issueDelete": { # "success": true # } # } # }

add_comment

Add a comment to an issue.

Parameters:

  • issue_id (string, required): Issue ID

  • body (string, required): Comment body (markdown supported)

Example:

# Simple comment comment = await add_comment( issue_id="issue-123", body="Working on this now" ) # Markdown comment comment = await add_comment( issue_id="issue-123", body="""## Update - Completed API integration - Testing authentication flow - Need to review error handling **ETA:** End of day""" ) # Returns: # { # "data": { # "commentCreate": { # "success": true, # "comment": { # "id": "comment-123", # "body": "Working on this now", # "createdAt": "2025-10-08T16:00:00Z" # } # } # } # }

search_issues

Search issues with full-text query.

Parameters:

  • query_text (string, required): Search query

  • first (int, optional): Number of results (default: 20)

Search Tips:

  • Search by title, description, or comments

  • Use quotes for exact phrases: "login bug"

  • Case-insensitive matching

  • Returns most relevant results first

Example:

# Simple search results = await search_issues(query_text="authentication") # Specific phrase results = await search_issues(query_text="\"user login\"", first=10) # Returns: # { # "data": { # "issueSearch": { # "nodes": [ # { # "id": "issue-123", # "title": "Fix authentication flow", # "description": "User login not working...", # "state": {"name": "In Progress"}, # "assignee": {"name": "Jane Smith"}, # "url": "https://linear.app/company/issue/ENG-123" # } # ] # } # } # }

Project Management

list_projects

List all projects in your workspace.

Parameters:

  • team_id (string, optional): Filter by team ID

  • first (int, optional): Number of projects to return (default: 50)

Example:

# All projects projects = await list_projects(first=20) # Team projects projects = await list_projects(team_id="team-123") # Returns: # { # "data": { # "projects": { # "nodes": [ # { # "id": "proj-123", # "name": "Q4 2025 Release", # "description": "Major feature release...", # "state": "started", # "progress": 0.65, # "targetDate": "2025-12-31", # "lead": { # "id": "user-456", # "name": "John Doe" # }, # "createdAt": "2025-09-01T00:00:00Z" # } # ] # } # } # }

get_project

Get detailed project information.

Parameters:

  • project_id (string, required): Project ID

Example:

project = await get_project(project_id="proj-123") # Returns: # { # "data": { # "project": { # "id": "proj-123", # "name": "Mobile App Redesign", # "description": "Complete UI/UX overhaul...", # "state": "started", # "progress": 0.42, # "targetDate": "2026-01-31", # "startDate": "2025-10-01", # "lead": { # "id": "user-456", # "name": "Jane Smith" # }, # "teams": { # "nodes": [ # {"id": "team-123", "name": "Design"}, # {"id": "team-456", "name": "Engineering"} # ] # }, # "url": "https://linear.app/company/project/redesign" # } # } # }

create_project

Create a new project.

Parameters:

  • name (string, required): Project name

  • team_ids (list of strings, required): List of team IDs

  • description (string, optional): Project description

  • target_date (string, optional): Target completion date (YYYY-MM-DD)

  • lead_id (string, optional): Project lead user ID

Example:

# Simple project project = await create_project( name="API v2 Migration", team_ids=["team-123"] ) # Full project project = await create_project( name="Customer Portal", team_ids=["team-123", "team-456"], description="Self-service customer dashboard with billing and support", target_date="2026-03-31", lead_id="user-789" ) # Returns: # { # "data": { # "projectCreate": { # "success": true, # "project": { # "id": "proj-new-123", # "name": "Customer Portal", # "url": "https://linear.app/company/project/customer-portal" # } # } # } # }

list_milestones

List project milestones.

Parameters:

  • project_id (string, optional): Filter by project ID

  • first (int, optional): Number of milestones (default: 50)

Example:

# All milestones milestones = await list_milestones(first=20) # Project milestones milestones = await list_milestones(project_id="proj-123") # Returns: # { # "data": { # "projectMilestones": { # "nodes": [ # { # "id": "milestone-123", # "name": "Beta Release", # "description": "Feature-complete beta version", # "targetDate": "2025-11-30", # "project": { # "id": "proj-123", # "name": "Q4 Release" # } # } # ] # } # } # }

get_roadmap

Get roadmap view of all projects.

Parameters:

  • first (int, optional): Number of items (default: 50)

Example:

roadmap = await get_roadmap(first=30) # Returns: # { # "data": { # "projects": { # "nodes": [ # { # "id": "proj-123", # "name": "Mobile App v2", # "description": "Next generation mobile experience", # "state": "started", # "progress": 0.35, # "targetDate": "2026-02-28", # "startDate": "2025-10-01", # "lead": {"name": "Jane Smith"} # } # ] # } # } # }

Team Management

list_teams

List all teams in your workspace.

Example:

teams = await list_teams() # Returns: # { # "data": { # "teams": { # "nodes": [ # { # "id": "team-123", # "name": "Engineering", # "key": "ENG", # "description": "Product development team", # "private": false, # "createdAt": "2025-01-01T00:00:00Z" # } # ] # } # } # }

get_team

Get detailed team information.

Parameters:

  • team_id (string, required): Team ID

Example:

team = await get_team(team_id="team-123") # Returns: # { # "data": { # "team": { # "id": "team-123", # "name": "Engineering", # "key": "ENG", # "description": "Product development team", # "private": false, # "members": { # "nodes": [ # { # "id": "user-123", # "name": "Jane Smith", # "email": "jane@company.com" # } # ] # }, # "projects": { # "nodes": [ # {"id": "proj-123", "name": "Q4 Release"} # ] # } # } # } # }

Sprint Management

list_cycles

List sprint cycles.

Parameters:

  • team_id (string, optional): Filter by team ID

  • first (int, optional): Number of cycles (default: 20)

Example:

# All cycles cycles = await list_cycles(first=10) # Team cycles cycles = await list_cycles(team_id="team-123") # Returns: # { # "data": { # "cycles": { # "nodes": [ # { # "id": "cycle-123", # "number": 42, # "name": "Sprint 42", # "startsAt": "2025-10-07T00:00:00Z", # "endsAt": "2025-10-20T23:59:59Z", # "progress": 0.58, # "completedIssueCount": 12, # "issueCount": 18, # "team": { # "id": "team-123", # "name": "Engineering" # } # } # ] # } # } # }

get_cycle

Get detailed cycle information.

Parameters:

  • cycle_id (string, required): Cycle ID

Example:

cycle = await get_cycle(cycle_id="cycle-123") # Returns: # { # "data": { # "cycle": { # "id": "cycle-123", # "number": 42, # "name": "Sprint 42", # "description": "Focus on authentication improvements", # "startsAt": "2025-10-07T00:00:00Z", # "endsAt": "2025-10-20T23:59:59Z", # "progress": 0.58, # "completedIssueCount": 12, # "issueCount": 18, # "team": { # "id": "team-123", # "name": "Engineering" # }, # "url": "https://linear.app/company/cycle/42" # } # } # }

Labels & Organization

list_labels

List all labels.

Parameters:

  • team_id (string, optional): Filter by team ID

Example:

# All labels labels = await list_labels() # Team labels labels = await list_labels(team_id="team-123") # Returns: # { # "data": { # "issueLabels": { # "nodes": [ # { # "id": "label-123", # "name": "bug", # "description": "Something isn't working", # "color": "#ff0000", # "team": { # "id": "team-123", # "name": "Engineering" # } # } # ] # } # } # }

create_label

Create a new label.

Parameters:

  • name (string, required): Label name

  • team_id (string, required): Team ID

  • color (string, optional): Hex color code (e.g., "#FF0000")

  • description (string, optional): Label description

Example:

# Simple label label = await create_label( name="security", team_id="team-123" ) # Full label label = await create_label( name="performance", team_id="team-123", color="#FFA500", description="Performance optimization tasks" ) # Returns: # { # "data": { # "issueLabelCreate": { # "success": true, # "issueLabel": { # "id": "label-new-123", # "name": "performance", # "color": "#FFA500" # } # } # } # }

Common Workflows

Daily Standup Preparation

# Get team's current sprint cycles = await list_cycles(team_id="team-123", first=1) current_cycle = cycles["data"]["cycles"]["nodes"][0] # Get my active issues my_issues = await list_issues( assignee_id="user-456", state="started", first=10 ) # Check recently completed issues completed = await list_issues( assignee_id="user-456", state="completed", first=5 )

Sprint Planning

# Get upcoming cycle cycle = await get_cycle(cycle_id="cycle-123") # Review backlog issues backlog = await list_issues( team_id="team-123", state="backlog", first=50 ) # Create sprint issues for item in sprint_plan: issue = await create_issue( team_id="team-123", title=item["title"], description=item["description"], priority=item["priority"], assignee_id=item["assignee"] )

Bug Triage

# Get all bugs bugs = await search_issues(query_text="bug", first=30) # Or use label filter bugs = await list_issues(label_id="label-bug-123") # Prioritize urgent bugs for bug in urgent_bugs: await update_issue( issue_id=bug["id"], priority=1, state_id="state-started" ) await add_comment( issue_id=bug["id"], body="Escalated to urgent - investigating now" )

Project Status Update

# Get project details project = await get_project(project_id="proj-123") # Get project milestones milestones = await list_milestones(project_id="proj-123") # Get issues for project issues = await list_issues(project_id="proj-123", first=100) # Calculate metrics total = len(issues["data"]["issues"]["nodes"]) completed = len([i for i in issues["data"]["issues"]["nodes"] if i["state"]["type"] == "completed"]) progress = completed / total if total > 0 else 0

Roadmap Planning

# Get all active projects roadmap = await get_roadmap(first=50) # Create new quarterly project project = await create_project( name="Q1 2026 Infrastructure", team_ids=["team-123"], description="Scale infrastructure for 10x growth", target_date="2026-03-31", lead_id="user-789" ) # Add milestones (would need milestone creation tool) # Add initial issues for initiative in initiatives: await create_issue( team_id="team-123", project_id=project["data"]["projectCreate"]["project"]["id"], title=initiative["title"], description=initiative["description"] )

Team Performance Metrics

# Get team info team = await get_team(team_id="team-123") # Get current cycle cycles = await list_cycles(team_id="team-123", first=1) cycle = cycles["data"]["cycles"]["nodes"][0] # Calculate velocity completed = cycle["completedIssueCount"] total = cycle["issueCount"] velocity = completed / total if total > 0 else 0 # Get member contributions for member in team["data"]["team"]["members"]["nodes"]: member_issues = await list_issues( assignee_id=member["id"], first=100 )

Issue States

Linear uses a workflow with these standard state types:

  • backlog: Not yet scheduled

  • unstarted: Planned but not started

  • started: Currently in progress

  • completed: Done and verified

  • canceled: Won't be completed

Teams can customize state names (e.g., "In Review", "Testing") while keeping these types.

Priority Levels

Level

Name

Color

Use Case

0

None

Gray

Default, no urgency

1

Urgent

Red

Critical issues, production down

2

High

Orange

Important features, significant bugs

3

Medium

Yellow

Standard work, normal priority

4

Low

Blue

Nice-to-have, low impact

Best Practices

  1. Use team_id filters: Narrow down results for better performance

  2. Pagination: Use first parameter to limit results

  3. Specific queries: Request only the fields you need

  4. Batch operations: Group related changes together

  5. State management: Follow your team's workflow states

  6. Labels: Use consistent labeling for better filtering

  7. Search wisely: Use specific terms for better search results

  8. Cache data: Don't repeatedly fetch unchanged data

  9. Error handling: Implement retries with backoff

  10. Monitor rate limits: Track API usage

GraphQL Tips

Request Only Needed Fields

# Good - minimal fields query = """ query { issues(first: 10) { nodes { id title } } } """ # Avoid - too many unnecessary fields query = """ query { issues(first: 10) { nodes { id title description priority state { ... } assignee { ... } # many more fields } } } """

Use Filters Effectively

# Good - specific filters issues = await list_issues( team_id="team-123", state="started", first=10 ) # Less efficient - fetch everything then filter all_issues = await list_issues(first=1000) # then filter in Python

Pagination

# For large datasets, use pagination first_page = await list_issues(first=50) # Get cursor from last item for next page # Linear supports cursor-based pagination

Error Handling

Common GraphQL errors:

  • Authentication failed: Invalid or expired API key

  • Not found: Resource ID doesn't exist

  • Rate limited: Too many requests

  • Validation error: Invalid input parameters

  • Insufficient permissions: User lacks access

All tools return GraphQL response format:

{ "data": { ... }, "errors": [ { "message": "Error description", "extensions": { "code": "ERROR_CODE" } } ] }

API Documentation

Support

-
security - not tested
A
license - permissive license
-
quality - not tested

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

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