mcpField
L
linear-api

linear-api

github.com/anthropics生产力7438 次安装3 周前更新
代码仓库

关于 linear-api

用于通过 Linear GraphQL API 查询和管理工作的技能,涵盖问题搜索、筛选、创建、更新和评论,并提供列表脚本及项目发现方法。

主要功能

  1. 01搜索和读取问题
  2. 02筛选并分页列出问题
  3. 03创建和更新问题
  4. 04添加问题评论
  5. 05发现团队和工作流

适用场景

  1. 01查找未解决或已分配的工作
  2. 02创建或更新 Linear 问题
  3. 03查看项目和团队信息

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/anthropics/claude-tag-plugins --skill linear-api

安装后在 Claude Code 中输入/linear-api确认已加载。

附带文件references/api.mdscripts/linear_issues.sh

详细介绍

Linear 只有一个 GraphQL 端点——没有 REST API。所有读取操作都是 query,所有写入操作都是 mutation,而且都发送到同一个 URL。不要寻找 /api/v1/issues 之类的路径;它们并不存在。

POST https://api.linear.app/graphql

两种 ID 系统并存:

  • UUID(id)——API 在所有查找和变更操作中使用的 ID。
  • 标识符(identifier,例如 ENG-123)——UI 中显示的人类可读键。可以通过标识符获取 issue:issue(id: "ENG-123")(Linear 接受这两种 ID)。

请求设置

身份验证由运行时处理——凭据会注入发往此 API 的出站请求,因此无需进行任何设置。不要尝试创建、生成、刷新或验证令牌或密钥。凭据变量仅用于确保请求格式正确;如果某个变量未设置,可将其设为任意占位值。持续出现 401/403 错误表示此工作区未配置凭据——请报告这一情况,而不是排查身份验证问题。

Linear 要求将凭据直接作为值放入 Authorization 标头中,不带 Bearer 前缀——请严格按照以下示例发送标头。

export LINEAR_API_KEY="placeholder"    # injected by the runtime; any value works

健全性检查——确认工作区已连通,并查看配置的身份对应的是谁:

curl -sS "https://api.linear.app/graphql" \
  -H "Authorization: ${LINEAR_API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{"query": "{ viewer { id name email } }"}' | jq .

每个会话只需定义一次辅助函数,避免重复编写样板代码。它接受一个查询字符串和一个可选的变量 JSON 对象:

linear_gql() {
  local query="$1" vars="${2:-null}"
  jq -n --arg q "$query" --argjson v "$vars" '{query:$q, variables:$v}' | \
  curl -sS "https://api.linear.app/graphql" \
    -H "Authorization: ${LINEAR_API_KEY}" \
    -H "Content-Type: application/json" \
    -d @-
}

务必先检查 .errors,再信任 .data——即使请求失败,GraphQL 仍会返回 HTTP 200:

linear_gql '{ viewer { id } }' | jq 'if .errors then .errors else .data end'

核心操作

1. 我是谁 / 分配给我的任务有哪些
linear_gql '{
  viewer {
    id name email
    assignedIssues(first: 25, orderBy: updatedAt, filter: {state: {type: {nin: ["completed","canceled"]}}}) {
      nodes { identifier title state { name type } priority team { key } updatedAt }
    }
  }
}' | jq '.errors // .data.viewer'

优先级:0 = 无优先级,1 = 紧急,2 = 高,3 = 中,4 = 低。

2. 搜索 issue
linear_gql 'query($q: String!) {
  searchIssues(term: $q, first: 25) {
    nodes { identifier title state { name } assignee { name } team { key } priority url }
  }
}' '{"q": "login crash"}' | jq '.data.searchIssues.nodes'
3. 获取单个 issue(通过标识符或 UUID)
linear_gql 'query($id: String!) {
  issue(id: $id) {
    id identifier title description url priority estimate
    state { name type }
    assignee { name email }
    creator { name }
    team { id key name }
    project { id name }
    cycle { id number name }
    labels { nodes { name color } }
    comments(first: 50) { nodes { body user { name } createdAt } }
    children { nodes { identifier title state { name } } }
    parent { identifier title }
    createdAt updatedAt dueDate
  }
}' '{"id": "ENG-123"}' | jq '.data.issue'
4. 列出和筛选 issue(scripts/linear_issues.sh)

通过附带的脚本列出 issue(路径相对于此技能的目录):脚本会根据标志构建 IssueFilter,通过 pageInfo.endCursor 分页,检查每个响应中的 .errors,并输出 TSV 或 JSONL。

scripts/linear_issues.sh --team ENG --state-type started --assignee me --limit 100
  • 所有筛选标志均为可选项,且会组合使用(AND):--team KEY、--state NAME、--state-type TYPE(取值为 backlog、unstarted、started、completed、canceled、triage、duplicate 之一)、 --assignee EMAIL(或 me——先解析查看者 ID)、--label NAME。实例的具体信息来自上文的 LINEAR_API_KEY。
  • --query TEXT 会对标题和描述进行不区分大小写的子字符串匹配;与其他标志组合使用时,会包装为 and: [ {…flags}, {or: [title, description]} ]。
  • --limit N 限制获取的 issue 总数(默认值为 50,0 = 全部);--page-size N(最大值为 100) 设置每页大小;--json 会为每个 issue 输出一个 JSON 对象,而非带有标题行的 TSV (字段为 identifier、title、state、assignee、updatedAt、url)。行数会输出到 stderr。
  • 退出代码:0 表示成功;1 表示请求失败、GraphQL .errors 或参数错误——API 返回的消息和 extensions.code 会输出到 stderr。

如果脚本报错,请阅读脚本——它只是普通的 curl + jq——并参照 references/api.md 进行调试。

对于标志无法涵盖的筛选器结构——例如 lte/gte 之类的比较运算符、or 分组、嵌套的 some/every——请使用 linear_gql,自行传入 filter 对象:

linear_gql 'query($teamKey: String!) {
  issues(first: 50, orderBy: updatedAt, filter: {
    team: { key: { eq: $teamKey } }
    priority: { lte: 2, gte: 1 }
    state: { type: { nin: ["completed", "canceled"] } }
  }) { nodes { identifier title priority state { name } assignee { name } } }
}' '{"teamKey": "ENG"}' | jq '.data.issues.nodes'

完整的筛选器语法:references/api.md 的 Filter operators 章节。

5. 创建 issue

需要团队的 UUID(不是团队键)。获取一次即可(示例 8),然后:

linear_gql 'mutation($input: IssueCreateInput!) {
  issueCreate(input: $input) {
    success
    issue { id identifier url }
  }
}' '{"input": {
  "teamId": "TEAM_UUID",
  "title": "Crash on empty input",
  "description": "Steps to reproduce…\n\n1. …",
  "priority": 2,
  "assigneeId": "USER_UUID",
  "labelIds": ["LABEL_UUID"]
}}' | jq '.data.issueCreate'

description 是 Markdown。

6. 更新 issue(更改状态、受派人、优先级等)
linear_gql 'mutation($id: String!, $input: IssueUpdateInput!) {
  issueUpdate(id: $id, input: $input) {
    success
    issue { identifier state { name } assignee { name } }
  }
}' '{"id": "ENG-123", "input": {"stateId": "STATE_UUID", "priority": 1}}' | jq '.data.issueUpdate'

要将 issue 移至“已完成”/“进行中”等状态,需要工作流的状态 UUID,该 UUID 因团队而异(示例 8 介绍了如何列出状态)。

7. 在 issue 上发表评论
linear_gql 'mutation($input: CommentCreateInput!) {
  commentCreate(input: $input) { success comment { id url } }
}' '{"input": {"issueId": "ISSUE_UUID", "body": "Reproduced on main — looking into it."}}' \
  | jq '.data.commentCreate'

body 是 Markdown。issueId 必须是 UUID,而不是标识符——请先使用示例 3 获取 UUID。

8. 发现团队、工作流状态、标签和成员

创建/更新 mutation 时需要这些 UUID。

linear_gql '{
  teams {
    nodes {
      id key name
      states { nodes { id name type position } }
      labels { nodes { id name color } }
      members { nodes { id name email } }
    }
  }
}' | jq '.data.teams.nodes'

状态 type ∈ backlog、unstarted、started、completed、canceled、triage、duplicate。

9. 项目和周期
linear_gql '{
  projects(first: 25, orderBy: updatedAt, filter: {status: {type: {neq: "completed"}}}) {
    nodes {
      id name description progress targetDate url
      status { name type }
      lead { name }
      teams { nodes { key } }
      issues(first: 5) { nodes { identifier title state { name } } }
    }
  }
}' | jq '.data.projects.nodes'

# current cycle for a team
linear_gql 'query($teamId: String!) {
  team(id: $teamId) {
    activeCycle {
      id number name startsAt endsAt progress
      issues { nodes { identifier title state { name } assignee { name } } }
    }
  }
}' '{"teamId": "TEAM_UUID"}' | jq '.data.team.activeCycle'

连接没有 totalCount 字段——要统计问题数量,请对 nodes 分页并计数,或读取 team { issueCount } 之类的聚合字段。例外:搜索载荷(searchIssues、searchProjects、searchDocuments)确实会返回 totalCount。

10. 工作区近期活动
linear_gql '{
  issues(first: 25, orderBy: updatedAt) {
    nodes { identifier title state { name } assignee { name } team { key } updatedAt }
  }
}' | jq '.data.issues.nodes'

分页

每个列表字段(issues、nodes、projects、comments 等)都是使用游标分页的连接。响应包含 pageInfo:

linear_gql 'query($cursor: String) {
  issues(first: 50, after: $cursor, orderBy: createdAt) {
    pageInfo { hasNextPage endCursor }
    nodes { identifier title }
  }
}' '{"cursor": null}' | jq '.data.issues'

循环执行:将 pageInfo.endCursor 作为 $cursor 传入,并在 hasNextPage 为 false 时停止。如果响应包含 .errors,或提取的游标为空或字面字符串 null,也应停止并打印响应内容——否则错误封装会导致无限循环。始终为循环设置最大页数限制。first 每页最多为 250 条(默认 50)。反向分页时使用 last / before。

速率限制

Linear 实施每用户请求限制和复杂度限制(大致与查询可能触及的节点数量成正比)。每个响应都包含:

X-RateLimit-Requests-Limit / -Remaining / -Reset
X-Complexity
X-RateLimit-Complexity-Limit / -Remaining / -Reset

API 密钥身份验证每小时可发起 5,000 次请求,获得 3,000,000 个复杂度点(OAuth 应用每位用户每小时可发起 5,000 次请求,获得 2,000,000 个点);单次查询不得超过 10,000 个点。超过限制时,Linear 会返回 HTTP 400(而不是 429),并附带 errors[].extensions.code: "RATELIMITED"。-Reset 标头中的值是 UTC 纪元毫秒——适用于 BSD/macOS 和 GNU date:

sleep $(( reset_ms / 1000 - $(date +%s) + 1 ))   # reset_ms from X-RateLimit-Requests-Reset

要降低复杂度,请请求更少的字段、限制 first:,并避免对不需要的连接进行深层嵌套(例如,不要在列表中的每个问题上都选择 comments)。

错误处理

大多数应用程序错误发生时,GraphQL 返回的 HTTP 状态码仍为 200——务必检查响应正文的 errors[]。

  • HTTP 400 + code: "GRAPHQL_VALIDATION_FAILED"——架构验证失败(字段/类型未知)。errors[].message 会指出错误字段——请修正查询,不要排查引号问题。
  • HTTP 500 + code: "GRAPHQL_VALIDATION_FAILED"——GraphQL 语法错误(括号不匹配等)。errors[].message 会显示解析位置。
  • HTTP 401——API 密钥无效或缺失。请检查 LINEAR_API_KEY。个人密钥应放在 Authorization: 中,不要加 Bearer 前缀。
  • HTTP 400 + code: "RATELIMITED"——触及速率或复杂度限制。等待至 X-RateLimit-…-Reset 指定的时间(纪元毫秒)后重试;或简化查询(减少字段、缩小 first:)。
  • errors[].extensions.code: "INVALID_INPUT" / "INPUT_ERROR"——验证失败。message 会指出错误字段。常见原因:在需要 UUID 的地方传入了键(ENG),或查询超过 10,000 点的复杂度上限。
  • errors[].extensions.code: "ENTITY_NOT_FOUND"——ID 不存在或不可见。请检查 ID。API 密钥的作用范围是整个工作区,但仍受团队/项目访问权限限制。
  • errors[].message: "Cannot query field …"——拼写错误或架构不匹配。字段不存在。请检查拼写;使用内省(references/api.md)。

即使 HTTP 状态和 errors 都正常,mutation 仍会返回 success 布尔值——确认写入前请先检查该值。

深入了解

references/api.md 提供了更完整的目录:所有主要对象类型及其字段、筛选运算符语法、webhook 配置、文件附件、表情回应、通知、收藏项、工作区/组织查询以及架构内省。若需要使用上述内容未涵盖的对象或 mutation,或需要了解创建/更新操作的确切输入格式,请阅读该文档。

评论

支持 Markdown