mcpField
L
linear

linear

github.com/nousresearch生産性258.9k80 回インストール今日更新
リポジトリ

linear について

Hermes から Linear を操作するユーザー向けのスキルです。GraphQL API の curl 例と、依存関係のない Python CLI で、issue、プロジェクト、ドキュメントを検索・変更できます。

主な機能

  1. 01issue の検索と照会
  2. 02issue の作成と更新
  3. 03チーム、プロジェクト、ワークフロー状態の管理
  4. 04Linear ドキュメントの取得
  5. 05依存関係のない Python CLI

活用シーン

  1. 01自分に割り当てられた issue の確認
  2. 02チームの issue を絞り込み状態を更新
  3. 03プロジェクト作成や issue へのコメント追加
  4. 04タイトルや slugId によるドキュメント検索

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/nousresearch/hermes-agent --skill linear

インストール後、Claude Code で/linearを入力して読み込まれたか確認します。

同梱ファイルscripts/linear_api.py

詳細

Linear — イシューとプロジェクトの管理

curl を使って GraphQL API 経由で Linear のイシュー、プロジェクト、チームを直接管理できます。MCP サーバー、OAuth フロー、追加の依存関係は不要です。

セットアップ

  1. Linear Settings > Account > Security & access > Personal API keys から個人用 API キーを取得します(URL:https://linear.app/settings/account/security)。注:組織レベルの Settings > API ページに表示されるのは OAuth アプリとワークスペースメンバー用のキーであり、個人用キーは表示されません。
  2. 環境で LINEAR_API_KEY を設定します(hermes setup または環境設定を使用します)。

API の基本

  • エンドポイント: https://api.linear.app/graphql(POST)
  • 認証ヘッダー: Authorization: $LINEAR_API_KEY(API キーには「Bearer」プレフィックスは不要です)
  • すべてのリクエストは POST で、Content-Type: application/json を指定します。
  • issue(id:) では UUID と短い識別子(例:ENG-123)のどちらも使用できます。

基本的な curl のパターン:

curl -s -X POST https://api.linear.app/graphql \
  -H "Authorization: $LINEAR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"query": "{ viewer { id name } }"}' | python3 -m json.tool

Python ヘルパースクリプト(使いやすい代替手段)

GraphQL を手書きせずに素早くワンライナーを実行できるよう、このスキルには標準ライブラリのみを使った Python CLI scripts/linear_api.py が付属しています。依存関係はありません。認証方法は同じです(LINEAR_API_KEY を読み込みます)。

SCRIPT=$(dirname "$(find ~/.hermes -path '*skills/productivity/linear/scripts/linear_api.py' 2>/dev/null | head -1)")/linear_api.py

python3 "$SCRIPT" whoami
python3 "$SCRIPT" list-teams
python3 "$SCRIPT" get-issue ENG-42
python3 "$SCRIPT" get-document 38359beef67c      # fetch a doc by slugId from the URL
python3 "$SCRIPT" raw 'query { viewer { name } }'

すべてのサブコマンド:whoami、list-teams、list-projects、list-states、list-issues、get-issue、search-issues、create-issue、update-issue、update-status、add-comment、list-documents、get-document、search-documents、raw。フラグを確認するには --help を実行してください。

GraphQL を組み立てずに手早く結果を得たい場合は、スクリプトを使ってください。スクリプトでラップされていないクエリが必要な場合や、フィルターをインラインで組み合わせたい場合は curl を使ってください。

ワークフローの状態

Linear では、type フィールドを持つ WorkflowState オブジェクトを使用します。状態タイプは 6 種類です。

タイプ説明
triage確認が必要な新規イシュー
backlog受け付け済みだが、まだ計画されていないイシュー
unstarted計画済み/着手可能だが、まだ開始されていないイシュー
started現在作業中のイシュー
completed完了
canceled対応しない

各チームには、それぞれ名前付きの状態があります(例:「In Progress」はタイプ started です)。イシューのステータスを変更するには、変更先の状態の stateId(UUID)が必要です。まずワークフローの状態をクエリしてください。

優先度の値: 0 = なし、1 = 緊急、2 = 高、3 = 中、4 = 低

よく使うクエリ

現在のユーザーを取得
curl -s -X POST https://api.linear.app/graphql \
  -H "Authorization: $LINEAR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"query": "{ viewer { id name email } }"}' | python3 -m json.tool
チーム一覧
curl -s -X POST https://api.linear.app/graphql \
  -H "Authorization: $LINEAR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"query": "{ teams { nodes { id name key } } }"}' | python3 -m json.tool
チームのワークフロー状態一覧
curl -s -X POST https://api.linear.app/graphql \
  -H "Authorization: $LINEAR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"query": "{ workflowStates(filter: { team: { key: { eq: \"ENG\" } } }) { nodes { id name type } } }"}' | python3 -m json.tool
イシュー一覧(最初の 20 件)
curl -s -X POST https://api.linear.app/graphql \
  -H "Authorization: $LINEAR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"query": "{ issues(first: 20) { nodes { identifier title priority state { name type } assignee { name } team { key } url } pageInfo { hasNextPage endCursor } } }"}' | python3 -m json.tool
自分に割り当てられたイシュー一覧
curl -s -X POST https://api.linear.app/graphql \
  -H "Authorization: $LINEAR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"query": "{ viewer { assignedIssues(first: 25) { nodes { identifier title state { name type } priority url } } } }"}' | python3 -m json.tool
1 件のイシューを取得(ENG-123 のような識別子で指定)
curl -s -X POST https://api.linear.app/graphql \
  -H "Authorization: $LINEAR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"query": "{ issue(id: \"ENG-123\") { id identifier title description priority state { id name type } assignee { id name } team { key } project { name } labels { nodes { name } } comments { nodes { body user { name } createdAt } } url } }"}' | python3 -m json.tool
テキストでイシューを検索
curl -s -X POST https://api.linear.app/graphql \
  -H "Authorization: $LINEAR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"query": "{ issueSearch(query: \"bug login\", first: 10) { nodes { identifier title state { name } assignee { name } url } } }"}' | python3 -m json.tool
状態タイプでイシューを絞り込み
curl -s -X POST https://api.linear.app/graphql \
  -H "Authorization: $LINEAR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"query": "{ issues(filter: { state: { type: { in: [\"started\"] } } }, first: 20) { nodes { identifier title state { name } assignee { name } } } }"}' | python3 -m json.tool
チームと担当者で絞り込み
curl -s -X POST https://api.linear.app/graphql \
  -H "Authorization: $LINEAR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"query": "{ issues(filter: { team: { key: { eq: \"ENG\" } }, assignee: { email: { eq: \"[email protected]\" } } }, first: 20) { nodes { identifier title state { name } priority } } }"}' | python3 -m json.tool
プロジェクト一覧
curl -s -X POST https://api.linear.app/graphql \
  -H "Authorization: $LINEAR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"query": "{ projects(first: 20) { nodes { id name description progress lead { name } teams { nodes { key } } url } } }"}' | python3 -m json.tool
チームメンバー一覧
curl -s -X POST https://api.linear.app/graphql \
  -H "Authorization: $LINEAR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"query": "{ users { nodes { id name email active } } }"}' | python3 -m json.tool
ラベル一覧
curl -s -X POST https://api.linear.app/graphql \
  -H "Authorization: $LINEAR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"query": "{ issueLabels { nodes { id name color } } }"}' | python3 -m json.tool

よく使うミューテーション

イシューを作成
curl -s -X POST https://api.linear.app/graphql \
  -H "Authorization: $LINEAR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "query": "mutation($input: IssueCreateInput!) { issueCreate(input: $input) { success issue { id identifier title url } } }",
    "variables": {
      "input": {
        "teamId": "TEAM_UUID",
        "title": "Fix login bug",
        "description": "Users cannot login with SSO",
        "priority": 2
      }
    }
  }' | python3 -m json.tool
イシューのステータスを更新

まず上記のワークフロー状態クエリで変更先の状態の UUID を取得し、その後に次を実行します。

curl -s -X POST https://api.linear.app/graphql \
  -H "Authorization: $LINEAR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"query": "mutation { issueUpdate(id: \"ENG-123\", input: { stateId: \"STATE_UUID\" }) { success issue { identifier state { name type } } } }"}' | python3 -m json.tool
イシューを割り当て
curl -s -X POST https://api.linear.app/graphql \
  -H "Authorization: $LINEAR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"query": "mutation { issueUpdate(id: \"ENG-123\", input: { assigneeId: \"USER_UUID\" }) { success issue { identifier assignee { name } } } }"}' | python3 -m json.tool
優先度を設定
curl -s -X POST https://api.linear.app/graphql \
  -H "Authorization: $LINEAR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"query": "mutation { issueUpdate(id: \"ENG-123\", input: { priority: 1 }) { success issue { identifier priority } } }"}' | python3 -m json.tool
コメントを追加
curl -s -X POST https://api.linear.app/graphql \
  -H "Authorization: $LINEAR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"query": "mutation { commentCreate(input: { issueId: \"ISSUE_UUID\", body: \"Investigated. Root cause is X.\" }) { success comment { id body } } }"}' | python3 -m json.tool
期限を設定
curl -s -X POST https://api.linear.app/graphql \
  -H "Authorization: $LINEAR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"query": "mutation { issueUpdate(id: \"ENG-123\", input: { dueDate: \"2026-04-01\" }) { success issue { identifier dueDate } } }"}' | python3 -m json.tool
イシューにラベルを追加
curl -s -X POST https://api.linear.app/graphql \
  -H "Authorization: $LINEAR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"query": "mutation { issueUpdate(id: \"ENG-123\", input: { labelIds: [\"LABEL_UUID_1\", \"LABEL_UUID_2\"] }) { success issue { identifier labels { nodes { name } } } } }"}' | python3 -m json.tool
イシューをプロジェクトに追加
curl -s -X POST https://api.linear.app/graphql \
  -H "Authorization: $LINEAR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"query": "mutation { issueUpdate(id: \"ENG-123\", input: { projectId: \"PROJECT_UUID\" }) { success issue { identifier project { name } } } }"}' | python3 -m json.tool
プロジェクトを作成
curl -s -X POST https://api.linear.app/graphql \
  -H "Authorization: $LINEAR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "query": "mutation($input: ProjectCreateInput!) { projectCreate(input: $input) { success project { id name url } } }",
    "variables": {
      "input": {
        "name": "Q2 Auth Overhaul",
        "description": "Replace legacy auth with OAuth2 and PKCE",
        "teamIds": ["TEAM_UUID"]
      }
    }
  }' | python3 -m json.tool

ドキュメント

Linear の ドキュメントは、イシューとあわせて保存される文章形式のドキュメント(RFC、仕様書、メモなど)です。専用の documents ルートクエリと、単一のドキュメントを取得する document(id:) があります。

ドキュメントの URL と slugId

ドキュメントの URL は次のような形式です。

https://linear.app/<workspace>/document/<slug>-<hexSlugId>

末尾の 16 進数の部分が slugId です。例:https://linear.app/nousresearch/document/rfc-hermes-permission-gateway-discord-38359beef67c → slugId は 38359beef67c です。

重要なスキーマの詳細: Markdown 本文は content フィールドにあります。ProseMirror JSON は contentState にあります(contentData ではありません。このフィールドは存在せず、API は 400 を返します)。

slugId でドキュメントを取得

document(id:) が受け付けるのは UUID のみです。URL の 16 進数のスラッグで取得するには、コレクションをフィルタリングします。

curl -s -X POST https://api.linear.app/graphql \
  -H "Authorization: $LINEAR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"query": "query($s: String!) { documents(filter: { slugId: { eq: $s } }, first: 1) { nodes { id title content contentState slugId url creator { name } project { name } updatedAt } } }", "variables": {"s": "38359beef67c"}}' \
  | python3 -m json.tool

または、Python ヘルパーを使います。

python3 scripts/linear_api.py get-document 38359beef67c
UUID でドキュメントを取得
curl -s -X POST https://api.linear.app/graphql \
  -H "Authorization: $LINEAR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"query": "{ document(id: \"11700cff-b514-4db3-afcc-3ed1afacba1c\") { title content url } }"}' \
  | python3 -m json.tool
最近のドキュメント一覧
curl -s -X POST https://api.linear.app/graphql \
  -H "Authorization: $LINEAR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"query": "{ documents(first: 25, orderBy: updatedAt) { nodes { id title slugId url updatedAt project { name } } } }"}' \
  | python3 -m json.tool
タイトルでドキュメントを検索する

Linear のスキーマには searchDocuments ルートがありません。代わりに、タイトルの部分一致フィルターを使用してください。

curl -s -X POST https://api.linear.app/graphql \
  -H "Authorization: $LINEAR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"query": "{ documents(filter: { title: { containsIgnoreCase: \"RFC\" } }, first: 25) { nodes { title slugId url } } }"}' \
  | python3 -m json.tool

ページネーション

Linear は Relay 形式のカーソルページネーションを使用します。

# First page
curl -s -X POST https://api.linear.app/graphql \
  -H "Authorization: $LINEAR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"query": "{ issues(first: 20) { nodes { identifier title } pageInfo { hasNextPage endCursor } } }"}' | python3 -m json.tool

# Next page — use endCursor from previous response
curl -s -X POST https://api.linear.app/graphql \
  -H "Authorization: $LINEAR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"query": "{ issues(first: 20, after: \"CURSOR_FROM_PREVIOUS\") { nodes { identifier title } pageInfo { hasNextPage endCursor } } }"}' | python3 -m json.tool

デフォルトのページサイズ:50。最大:250。結果を制限するには、必ず first: N を使用してください。

フィルタリングのリファレンス

比較演算子:eq、neq、in、nin、lt、lte、gt、gte、contains、startsWith、containsIgnoreCase

OR 条件を指定するには、フィルターを or: [...] で結合します(フィルターオブジェクト内のデフォルトは AND です)。

一般的なワークフロー

  1. チームをクエリすると、チーム ID とキーを取得できます
  2. 対象チームのワークフロー状態をクエリすると、状態 UUID を取得できます
  3. 課題を一覧表示または検索すると、対応が必要なものを見つけられます
  4. 課題を作成する際は、チーム ID、タイトル、説明、優先度を指定します
  5. ステータスを更新するには、stateId に対象のワークフロー状態を設定します
  6. コメントを追加すると、進捗を記録できます
  7. 完了としてマークするには、stateId にチームの「完了」タイプの状態を設定します

レート制限

  • API キーごとに 1 時間あたり 5,000 リクエスト
  • 1 時間あたり 3,000,000 複雑度ポイント
  • 結果を制限して複雑度コストを削減するには、first: N を使用してください
  • レスポンスヘッダー X-RateLimit-Requests-Remaining を監視してください

重要な注意事項

  • API 呼び出しには、必ず curl を使う terminal ツールを使用してください。web_extract や browser は使用しないでください
  • GraphQL のレスポンスでは、必ず errors 配列を確認してください。HTTP 200 でもエラーが含まれている場合があります
  • 課題の作成時に stateId を省略すると、Linear は最初のバックログ状態をデフォルトとして設定します
  • description フィールドは Markdown に対応しています
  • 読みやすく JSON レスポンスを整形するには、python3 -m json.tool または jq を使用してください

コメント