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
Environment Variables
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:
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:
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:
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:
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:
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:
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:
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:
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
Use team_id filters: Narrow down results for better performance
Pagination: Use first
parameter to limit results
Specific 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 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