Provides comprehensive project management capabilities including issue tracking, sprint planning, team collaboration, project management with milestones and roadmaps, label organization, and team performance metrics through Linear's GraphQL API.
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@Linear MCP Serverlist all issues assigned to me that are in progress"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
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:
Go to linear.app/settings/api
Sign in to your Linear workspace
Click "Create new key" or "Personal API keys"
Give your key a descriptive name
Copy the generated key (starts with
lin_api_)Store as
LINEAR_API_KEY
API Key Format:
Format:
lin_api_xxxxxxxxxxxxxxxxxxxxxxxxKeep 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/graphqlSpecify 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 IDproject_id(string, optional): Filter by project IDassignee_id(string, optional): Filter by assignee user IDlabel_id(string, optional): Filter by label IDstate(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 IDtitle(string, required): Issue titledescription(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 IDproject_id(string, optional): Project IDlabel_ids(list of strings, optional): List of label IDs
Priority Levels:
0: No priority1: 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 IDtitle(string, optional): Updated titledescription(string, optional): Updated descriptionpriority(int, optional): Updated priority (0-4)state_id(string, optional): Updated state IDassignee_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 IDbody(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 queryfirst(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 IDfirst(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 nameteam_ids(list of strings, required): List of team IDsdescription(string, optional): Project descriptiontarget_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 IDfirst(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 IDfirst(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 nameteam_id(string, required): Team IDcolor(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 0Roadmap 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
Use team_id filters: Narrow down results for better performance
Pagination: Use
firstparameter to limit resultsSpecific queries: Request only the fields you need
Batch operations: Group related changes together
State management: Follow your team's workflow states
Labels: Use consistent labeling for better filtering
Search wisely: Use specific terms for better search results
Cache data: Don't repeatedly fetch unchanged data
Error handling: Implement retries with backoff
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 PythonPagination
# For large datasets, use pagination
first_page = await list_issues(first=50)
# Get cursor from last item for next page
# Linear supports cursor-based paginationError 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
This server cannot be installed
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.