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. 02Linear の問題を作成・更新する
  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

2 つの ID システムが併存しています。

  • UUID(id)— API が検索やミューテーションで常に使用する ID です。
  • 識別子(identifier、例:ENG-123)— UI に表示される、人が読みやすいキーです。issue(id: "ENG-123") で識別子を使って課題を取得できます(Linear はどちらも受け付けます)。

リクエストの設定

認証はランタイムが処理します。認証情報はこの 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 @-
}

.data を信頼する前に、必ず .errors を確認してください — 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. 課題を検索する
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. 1 件の課題を取得する(識別子または 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. 課題を一覧表示して絞り込む(scripts/linear_issues.sh)

同梱スクリプトを使って課題を一覧表示します(パスはこのスキルのディレクトリからの相対パスです)。フラグから 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 は取得する課題の合計数に上限を設定します(デフォルトは 50、0 = すべて)。--page-size N(最大 100)は 1 ページあたりのサイズを設定します。--json は、ヘッダー(identifier、title、state、assignee、updatedAt、url)付きの TSV の代わりに、課題ごとに 1 つの JSON オブジェクトを出力します。行数は 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. 課題を作成する

チームの 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. 課題を更新する(状態、担当者、優先度などを変更する)
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'

「完了」/「進行中」などに移動するには、ワークフローの状態 UUIDが必要です。これはチームごとに異なります(取得方法はレシピ 8 を参照してください)。

7. 課題にコメントする
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 で取得してください。

8. チーム、ワークフローの状態、ラベル、メンバーを確認する

作成/更新ミューテーションでこれらの 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 は1ページあたり最大 250 件(デフォルトは50件)です。逆方向にページネーションするには、last/before を使用してください。

レート制限

Linear にはユーザーごとのリクエスト制限と複雑度制限(クエリが対象とし得るノード数におおむね比例)が適用されます。すべてのレスポンスには次の情報が含まれます。

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

API キー認証では、1時間あたり5,000件のリクエストと3,000,000複雑度ポイントが許可されます(OAuth アプリでは、ユーザーごとに1時間あたり5,000件のリクエストと2,000,000ポイント)。1つのクエリで10,000ポイントを超えることはできません。制限を超えると、Linear は HTTP 429 ではなく 400 を返し、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(エポック ms)まで待ってから再試行するか、クエリを簡素化してください(フィールドを減らす、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 に問題がない場合でも success のブール値を返します。書き込みが反映されたと判断する前に、必ず確認してください。

詳細を見る

references/api.md には、主要なオブジェクト型とそのフィールド、フィルター演算子の構文、Webhook の設定、ファイル添付、リアクション、通知、お気に入り、ワークスペース/組織のクエリ、スキーマイントロスペクションなど、より詳しいカタログがあります。ここまでに記載されていないオブジェクトやミューテーションが必要な場合や、作成/更新に必要な入力の正確な形式を確認したい場合は、こちらを参照してください。

コメント