From openhands-skills
Interact with Linear project management via GraphQL API: query assigned/open issues by priority/identifier, fetch details, update status, create tickets, manage workflows. Use for Linear tickets, sprints, project tracking.
npx claudepluginhub openhands/extensionsThis skill uses the workspace's default tool permissions.
<IMPORTANT>
Manages Linear issues, projects, and teams using MCP tools, linear CLI, and GraphQL API. Create, update, query issues; handle labels, status, references, and backlogs.
Manages Linear issues via linearis CLI: list, search, read details, create with options, update title/description/status/labels/assignee. Integrates with dev workflows.
Manages issues, projects, and team workflows in Linear via MCP tools like list_issues, create_issue, update_issue. Useful for reading, creating, or updating tickets.
Share bugs, ideas, or general feedback.
[ -n "$LINEAR_API_KEY" ] && echo "LINEAR_API_KEY is set" || echo "LINEAR_API_KEY is NOT set"
If LINEAR_API_KEY is missing, ask the user to provide it before proceeding.
Linear uses two types of identifiers for issues:
ALL-1234): Displayed to users, used in search queries. This is the team key + number.a1b2c3d4-e5f6-7890-abcd-ef1234567890): Required for all mutations (update, comment, etc.). Returned as id in query results.Important workflow: When working with issues, you must:
id (UUID) from the query resultAll Linear API requests use GraphQL with the API key in the Authorization header:
curl -s -X POST https://api.linear.app/graphql \
-H "Content-Type: application/json" \
-H "Authorization: $LINEAR_API_KEY" \
-d '{"query": "YOUR_GRAPHQL_QUERY"}'
curl -s -X POST https://api.linear.app/graphql \
-H "Content-Type: application/json" \
-H "Authorization: $LINEAR_API_KEY" \
-d '{
"query": "query { viewer { assignedIssues(first: 50, filter: { state: { type: { nin: [\"completed\", \"canceled\"] } } }) { nodes { id identifier title priority priorityLabel state { name type } description createdAt updatedAt } } } }"
}' | jq '.data.viewer.assignedIssues.nodes'
Priority values: 0 = No priority, 1 = Urgent, 2 = High, 3 = Medium, 4 = Low
curl -s -X POST https://api.linear.app/graphql \
-H "Content-Type: application/json" \
-H "Authorization: $LINEAR_API_KEY" \
-d '{
"query": "query { viewer { assignedIssues(first: 50, filter: { priority: { lte: 2 }, state: { type: { nin: [\"completed\", \"canceled\"] } } }) { nodes { id identifier title priority priorityLabel state { name } } } } }"
}' | jq '.data.viewer.assignedIssues.nodes'
curl -s -X POST https://api.linear.app/graphql \
-H "Content-Type: application/json" \
-H "Authorization: $LINEAR_API_KEY" \
-d '{
"query": "query { issue(id: \"ISSUE_UUID\") { id identifier title description state { name } priority assignee { name email } labels { nodes { name } } comments { nodes { body createdAt user { name } } } } }"
}' | jq '.data.issue'
curl -s -X POST https://api.linear.app/graphql \
-H "Content-Type: application/json" \
-H "Authorization: $LINEAR_API_KEY" \
-d '{
"query": "query { issueSearch(query: \"ALL-1234\", first: 5) { nodes { id identifier title state { name } } } }"
}' | jq '.data.issueSearch.nodes'
First, get available workflow states:
curl -s -X POST https://api.linear.app/graphql \
-H "Content-Type: application/json" \
-H "Authorization: $LINEAR_API_KEY" \
-d '{
"query": "query { workflowStates { nodes { id name type } } }"
}' | jq '.data.workflowStates.nodes'
Then update the issue:
curl -s -X POST https://api.linear.app/graphql \
-H "Content-Type: application/json" \
-H "Authorization: $LINEAR_API_KEY" \
-d '{
"query": "mutation { issueUpdate(id: \"ISSUE_UUID\", input: { stateId: \"STATE_UUID\" }) { success issue { identifier state { name } } } }"
}' | jq '.data.issueUpdate'
curl -s -X POST https://api.linear.app/graphql \
-H "Content-Type: application/json" \
-H "Authorization: $LINEAR_API_KEY" \
-d '{
"query": "mutation { commentCreate(input: { issueId: \"ISSUE_UUID\", body: \"Your comment here\" }) { success comment { id body } } }"
}' | jq '.data.commentCreate'
curl -s -X POST https://api.linear.app/graphql \
-H "Content-Type: application/json" \
-H "Authorization: $LINEAR_API_KEY" \
-d '{
"query": "mutation { issueCreate(input: { teamId: \"TEAM_UUID\", title: \"Issue Title\", description: \"Issue description\", priority: 2 }) { success issue { identifier title url } } }"
}' | jq '.data.issueCreate'
This example shows the complete flow to change an issue's state using its human-readable identifier:
# Search for issue ALL-1234 and extract its UUID
curl -s -X POST https://api.linear.app/graphql \
-H "Content-Type: application/json" \
-H "Authorization: $LINEAR_API_KEY" \
-d '{
"query": "query { issueSearch(query: \"ALL-1234\", first: 1) { nodes { id identifier title state { name } } } }"
}' | jq '.data.issueSearch.nodes[0]'
# Save the "id" value (UUID) from the response
# List all workflow states to find the "In Progress" state UUID
curl -s -X POST https://api.linear.app/graphql \
-H "Content-Type: application/json" \
-H "Authorization: $LINEAR_API_KEY" \
-d '{
"query": "query { workflowStates { nodes { id name type } } }"
}' | jq '.data.workflowStates.nodes[] | select(.name == "In Progress")'
# Save the "id" value of the desired state
# Use the issue UUID and state UUID from previous steps
curl -s -X POST https://api.linear.app/graphql \
-H "Content-Type: application/json" \
-H "Authorization: $LINEAR_API_KEY" \
-d '{
"query": "mutation { issueUpdate(id: \"ISSUE_UUID_FROM_STEP_1\", input: { stateId: \"STATE_UUID_FROM_STEP_2\" }) { success issue { identifier state { name } } } }"
}' | jq '.data.issueUpdate'
curl -s -X POST https://api.linear.app/graphql \
-H "Content-Type: application/json" \
-H "Authorization: $LINEAR_API_KEY" \
-d '{
"query": "query { teams { nodes { id name key } } }"
}' | jq '.data.teams.nodes'
| Priority | Label | Recommended Action |
|---|---|---|
| 1 | Urgent | Work on immediately |
| 2 | High | Work on first |
| 3 | Medium | Normal priority |
| 4 | Low | When time permits |
| 0 | None | Backlog |
backlog - Not yet startedunstarted - Todostarted - In Progresscompleted - Donecanceled - Won't do