Use Cases (更新: 2026/6/2)

Claude CodeでGitHub API自動化:安全な設計と実装例

GitHub APIをClaude Codeで安全に自動化する実装ガイド。権限、ページング、Webhookまで解説。

Claude CodeでGitHub API自動化:安全な設計と実装例

GitHub APIは、Issue、Pull Request、Release、Repository設定、Webhookなどをコードから扱うための公式インターフェースです。Claude Codeと組み合わせると、手作業で毎朝確認していたリポジトリの状態、放置されたPR、Release noteの下書き、Issueの一次分類を短時間で自動化できます。

ただし、危ない自動化も同じ速度で作れてしまいます。典型例は、Personal Access Tokenをログに出す、repo全権限のclassic tokenを雑に使う、1ページ目だけ読んで「全件処理した」と思い込む、rate limitに当たって無限リトライする、Webhook署名を検証せず外部からの偽イベントを信じる、Claude Codeに破壊的な一括編集を丸投げする、といった失敗です。

この記事では、GitHub APIを初めて触る人にもわかるように、RESTとGraphQLの使い分け、tokenの権限、pagination、rate limit、Webhook、idempotency(同じイベントを2回受けても結果が壊れない性質)を整理します。そのうえで、Claude Codeに安全な制約を渡しながら、コピペで動かせるNode.js実装に落とし込みます。

公式情報は必ず最新を確認してください。入口はGitHub REST API docs、一覧取得時はREST APIのrate limit、Webhookを受けるならWebhook delivery validation、複雑な集計ではGitHub GraphQL APIを参照します。Claude Code側の運用は、Git workflow実践ガイドGitHub Actions高度活用も合わせて読むと、ローカルスクリプトからCIまでつながります。

まず全体像をつかむ

GitHub API自動化は、いきなり「Issueを閉じるbot」を作るより、読む処理から始めるほうが安全です。最初のゴールは、環境変数からtokenを読み、必要なendpointだけを呼び、結果を人間が確認できる形で出力することです。

flowchart LR
  A["Claude Codeに目的と禁止事項を渡す"] --> B["読み取り専用APIで確認"]
  B --> C["paginationとrate limitを実装"]
  C --> D["dry-runで差分を表示"]
  D --> E["最小権限tokenで本番実行"]
  E --> F["WebhookとCIで継続運用"]

REST APIは「このrepositoryのopen issueを一覧する」「このPRにlabelを付ける」のように、リソース単位で操作するのに向いています。URL、HTTP method、status codeが読みやすく、curlやNodeのfetchで始めやすいのが利点です。一方で、Issue、PR、review、author、label、milestoneを横断して1つの表にしたい場合は、RESTだと複数endpointを何度も呼ぶことがあります。

GraphQL APIは「必要なfieldだけを1回のqueryで取る」用途に向いています。リポジトリ横断の集計、dashboard、release note候補の抽出では便利です。ただし、query設計を間違えると複雑になり、権限やrate limitの考え方もRESTと少し違います。初心者はRESTで安全な基本形を作り、集計が増えたらGraphQLを検討する順番で十分です。

観点REST APIGraphQL API
向いている作業Issue作成、PR更新、Release作成など単体操作複数repositoryや関連情報をまとめた集計
学習しやすさURLとHTTP methodで追いやすいquery設計を覚える必要がある
Claude Codeへの頼み方endpointごとに小さく実装させるschemaと必要fieldを先に定義させる
注意点pagination漏れ、endpointの権限不足query肥大化、取得fieldの過不足

Tokenと権限を最初に絞る

GitHub APIで一番大きな事故は、tokenの扱いです。tokenはパスワードと同じです。Claude Codeに「動けばよいからtokenを直接書く」と頼むと、サンプルコード、ログ、テストsnapshot、READMEに混ざる危険があります。最初のルールは、GITHUB_TOKENなどの環境変数から読み、値を絶対に表示しないことです。

Personal Access Tokenを使う場合は、可能な限りfine-grained tokenを選び、対象repositoryとpermissionを限定します。Issue triageならIssues: Read and write、PR一覧を見るだけならPull requests: Read-only、Release note生成ならContents: ReadMetadata: Readから始めます。classic tokenのrepo scopeは便利ですが、個人アカウント配下のprivate repositoryに広く届きやすいため、検証用でも短命にしてください。

GitHub Actions内で動かす場合は、workflowのpermissionsを明示します。例えばhealth reportを作るだけならcontents: readpull-requests: readで足ります。Issueにlabelを付けるならissues: writeを足します。Claude Codeにworkflowを作らせるときも、「必要permissionを表にしてからYAMLを書いて」と頼むと、過剰権限を見つけやすくなります。

Claude Codeに渡す最初の依頼は、次のように制約を強く書きます。

GitHub APIを使うNode.jsスクリプトを作ってください。
要件:
- tokenはprocess.env.GITHUB_TOKENから読む。コード、ログ、エラー文にtoken値を出さない。
- まず読み取り専用にする。IssueやPRを更新する処理は禁止。
- owner/repoは環境変数から読む。
- fetchでGitHub REST APIを呼び、status codeとrate limit headerを扱う。
- paginationが必要なendpointでは全ページを読む。
- 実行方法と必要なGitHub権限をREADME用に短く書く。

このように「やってほしいこと」だけでなく「やってはいけないこと」を先に渡すのが、Claude Codeで安全な自動化を作るコツです。

コピペで動く最小の読み取りスクリプト

次の例は、Node.js 18以降のfetchを使ってopen issueを一覧します。GITHUB_TOKENを直接書かず、ログにも出しません。まずは読み取り専用tokenで動かしてください。

export GITHUB_TOKEN="github_pat_xxx"
export GITHUB_OWNER="octocat"
export GITHUB_REPO="Hello-World"
node scripts/list-open-issues.mjs
// scripts/list-open-issues.mjs
const { GITHUB_TOKEN, GITHUB_OWNER, GITHUB_REPO } = process.env;

if (!GITHUB_TOKEN || !GITHUB_OWNER || !GITHUB_REPO) {
  throw new Error("Set GITHUB_TOKEN, GITHUB_OWNER, and GITHUB_REPO.");
}

const apiVersion = "2026-03-10";

async function github(path, options = {}) {
  const response = await fetch(`https://api.github.com${path}`, {
    ...options,
    headers: {
      Accept: "application/vnd.github+json",
      Authorization: `Bearer ${GITHUB_TOKEN}`,
      "X-GitHub-Api-Version": apiVersion,
      "User-Agent": "claudecodelab-safe-github-api-example",
      ...(options.headers ?? {}),
    },
  });

  if (!response.ok) {
    const body = await response.text();
    throw new Error(`GitHub API ${response.status}: ${body.slice(0, 500)}`);
  }

  return response.json();
}

const issues = await github(
  `/repos/${encodeURIComponent(GITHUB_OWNER)}/${encodeURIComponent(GITHUB_REPO)}/issues?state=open&per_page=10`,
);

const rows = issues
  .filter((issue) => !issue.pull_request)
  .map((issue) => ({
    number: issue.number,
    title: issue.title,
    labels: issue.labels.map((label) => label.name).join(", "),
    updated: issue.updated_at,
  }));

console.table(rows);

この段階では、Issueを閉じたりlabelを付けたりしません。Claude Codeに次の修正を頼む場合も、「まずdry-runで対象一覧を表示し、人間が確認してからAPPLY=trueのときだけ更新する」と書きます。読み取りから書き込みへ進む境界を明確にするだけで、事故率は大きく下がります。

Paginationとrate limitは最初から入れる

GitHub APIの一覧endpointは、多くの場合pagination(結果をページに分けて返す仕組み)があります。per_page=100を付けても100件を超えたら次のページを読まなければなりません。1ページ目だけでrelease noteを作ると、古いPRやIssueが抜けます。stale判定では、対象が少なく見えて誤った判断になります。

rate limitは、短時間に呼びすぎたときの制限です。403429だけを見て即リトライを繰り返すと、さらに制限を悪化させます。retry-afterx-ratelimit-reset headerを見て待つ、最大リトライ回数を決める、処理対象を小分けにする、CIではスケジュールをずらす、といった設計が必要です。

次のhelperは、Link headerから次ページをたどり、rate limit時は最大2回だけ待って再試行します。更新系にも使えますが、まずは読み取り処理で検証してください。

// scripts/github-pages.mjs
const token = process.env.GITHUB_TOKEN;
if (!token) throw new Error("Set GITHUB_TOKEN.");

const apiBase = "https://api.github.com";
const apiVersion = "2026-03-10";

function sleep(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

function defaultHeaders() {
  return {
    Accept: "application/vnd.github+json",
    Authorization: `Bearer ${token}`,
    "X-GitHub-Api-Version": apiVersion,
    "User-Agent": "claudecodelab-pagination-example",
  };
}

function parseNextLink(linkHeader) {
  if (!linkHeader) return null;
  for (const part of linkHeader.split(",")) {
    const match = part.match(/<([^>]+)>;\s*rel="([^"]+)"/);
    if (match && match[2] === "next") return match[1];
  }
  return null;
}

async function githubRequest(url, options = {}, attempt = 0) {
  const response = await fetch(url, {
    ...options,
    headers: {
      ...defaultHeaders(),
      ...(options.headers ?? {}),
    },
  });

  if ((response.status === 403 || response.status === 429) && attempt < 2) {
    const retryAfterSeconds = Number(response.headers.get("retry-after") ?? "0");
    const resetSeconds = Number(response.headers.get("x-ratelimit-reset") ?? "0");
    const resetDelayMs = resetSeconds > 0 ? resetSeconds * 1000 - Date.now() : 0;
    const waitMs = Math.max(retryAfterSeconds * 1000, resetDelayMs, 0);

    if (waitMs > 0 && waitMs <= 10 * 60 * 1000) {
      await sleep(waitMs + 1000);
      return githubRequest(url, options, attempt + 1);
    }
  }

  if (!response.ok) {
    const body = await response.text();
    throw new Error(`GitHub API ${response.status}: ${body.slice(0, 500)}`);
  }

  return {
    data: await response.json(),
    nextUrl: parseNextLink(response.headers.get("link")),
  };
}

export async function paginate(path) {
  const items = [];
  let url = path.startsWith("http") ? path : `${apiBase}${path}`;

  while (url) {
    const page = await githubRequest(url);
    if (!Array.isArray(page.data)) {
      throw new Error("paginate() expected an array response.");
    }
    items.push(...page.data);
    url = page.nextUrl;
  }

  return items;
}

if (import.meta.url === `file://${process.argv[1]}`) {
  const owner = process.env.GITHUB_OWNER;
  const repo = process.env.GITHUB_REPO;
  if (!owner || !repo) throw new Error("Set GITHUB_OWNER and GITHUB_REPO.");

  const pulls = await paginate(
    `/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/pulls?state=open&per_page=100`,
  );
  console.table(pulls.map((pr) => ({ number: pr.number, title: pr.title, updated: pr.updated_at })));
}

Claude Codeには、このhelperを共通化したあとで「既存コードの全API呼び出しをこのhelper経由に統一して。ただし更新処理の追加は禁止」と頼むとよいです。作業後はrg "api.github.com"で直呼びが残っていないか確認させます。

Webhookは署名検証とidempotencyが必須

Webhookは、GitHub側のイベントを自分のサーバーで受ける仕組みです。PRが開かれたらlabel候補を出す、Issueが作られたらtriage queueに入れる、releaseが公開されたら通知する、といった用途に向いています。

Webhookで最初に守るべきことは、署名検証です。x-hub-signature-256を検証しないと、GitHub以外から送られた偽のJSONを本物として処理する可能性があります。次にidempotencyです。GitHubは同じdeliveryを再送することがあります。x-github-deliveryを保存し、同じIDを二度処理しない設計にします。

次の例はExpressでraw bodyを受け、Nodeのcryptoで署名を検証します。実運用ではseenDeliveriesをRedisやDBに置き換えてください。

npm install express
export GITHUB_WEBHOOK_SECRET="your-webhook-secret"
node webhook-server.mjs
// webhook-server.mjs
import crypto from "node:crypto";
import express from "express";

const secret = process.env.GITHUB_WEBHOOK_SECRET;
if (!secret) throw new Error("Set GITHUB_WEBHOOK_SECRET.");

const app = express();
const seenDeliveries = new Set();

function verifySignature(payloadBuffer, signatureHeader) {
  if (!signatureHeader) return false;

  const expected = `sha256=${crypto
    .createHmac("sha256", secret)
    .update(payloadBuffer)
    .digest("hex")}`;

  const actual = Buffer.from(signatureHeader, "utf8");
  const expectedBuffer = Buffer.from(expected, "utf8");

  return actual.length === expectedBuffer.length && crypto.timingSafeEqual(actual, expectedBuffer);
}

app.post("/github/webhook", express.raw({ type: "application/json" }), (req, res) => {
  const signature = req.get("x-hub-signature-256");
  if (!verifySignature(req.body, signature)) {
    return res.status(401).send("invalid signature");
  }

  const deliveryId = req.get("x-github-delivery");
  if (!deliveryId) return res.status(400).send("missing delivery id");

  if (seenDeliveries.has(deliveryId)) {
    return res.status(202).send("duplicate ignored");
  }
  seenDeliveries.add(deliveryId);

  const event = req.get("x-github-event");
  const payload = JSON.parse(req.body.toString("utf8"));

  console.log(
    JSON.stringify({
      event,
      deliveryId,
      repository: payload.repository?.full_name,
      action: payload.action,
    }),
  );

  return res.status(202).send("accepted");
});

app.listen(process.env.PORT ?? 3000, () => {
  console.log("Listening for GitHub webhooks.");
});

Claude CodeにWebhook処理を拡張させるときは、「署名検証より前にJSON parseしない」「delivery IDで重複を止める」「副作用はqueueに積むだけにする」と制約を入れます。Webhook handler内で直接大量のIssue更新を走らせると、失敗時の再送で同じ処理が重なります。

実用ユースケース4選

1つ目はIssue triage botです。新規Issueを読み、本文のテンプレート欠落、再現手順の有無、関連label候補を出します。最初はコメント投稿まで自動化せず、daily reportに候補を出すだけにします。十分に精度を確認したら、needs-reprobugなど限定labelだけを書き込む段階へ進めます。

2つ目はstale PR reporterです。30日以上更新がないPR、review requestedのまま止まっているPR、CIが失敗したままのPRを一覧します。重要なのは、いきなりcloseしないことです。reporterはSlackやIssueに一覧を出すだけにし、closeやlabel変更は別の承認付きコマンドに分けます。

3つ目はrelease note generatorです。前回tagから今回tagまでのmerged PRを集め、labelごとに「Added」「Fixed」「Changed」に分類します。GraphQLを使うとauthorやreview情報もまとめやすくなりますが、最初はRESTのcompare endpointやpull request一覧で十分です。Claude Codeには「生成文は下書きであり、公開releaseは人間が確認する」と明記します。

4つ目はdaily repository health reportです。open issue数、古いPR、失敗中のworkflow、Dependabot alert、直近release、未処理reviewを毎朝1枚にまとめます。経営や非エンジニアにも共有するなら、件数だけでなく「今日見るべき3件」を出すと行動につながります。こうした定期処理はClaude Code workflow automationreview workflow checklistと相性がよいです。

よくある失敗と防ぎ方

classic tokenをログに出す失敗は、最初に潰します。console.log(process.env)、HTTP request全体のdump、CIのdebug logは危険です。Claude Codeには「secret値を含む可能性があるobjectを丸ごとlogしない」と指示します。レビューではrg "GITHUB_TOKEN|Authorization|process.env"で確認します。

overbroad scopeも多いです。Issueを読むだけなのにrepo全権限、Release noteの下書きだけなのにcontents: write、PR reportだけなのにpull-requests: writeになっていないかを見ます。権限は「必要になったら足す」が基本です。

pagination漏れは、静かに品質を落とします。1ページ目だけ見て「stale PRは10件」と判断すると、101件目以降を見逃します。すべての一覧endpointにper_page=100Link header処理があるかを確認してください。

rate-limit loopは、CI時間とAPI枠を無駄にします。while (true)で即リトライする実装は避け、headerを見て待つ、最大回数で失敗させる、次回実行に回す設計にします。

Webhook署名未検証は、外部入力を信用する事故です。署名検証前にpayloadを処理しない、secretを長くランダムにする、delivery IDを記録する、失敗時に安全に終了する、という基本を守ります。

destructive bulk editsも危険です。古いIssueを一括closeする、全PRにlabelを付ける、全releaseを書き換える、といった処理は、dry-run、対象件数の上限、明示的なAPPLY=true、audit log、rollback方針を入れてから実行します。Claude Codeには「20件を超える更新は停止してsummaryだけ出す」など、機械的なブレーキを指定します。

Claude Codeに任せる範囲を設計する

Claude Codeは、API client、pagination helper、テスト、README、GitHub Actions workflowの雛形を素早く作るのに向いています。一方で、token発行、production権限の承認、破壊的な更新の最終判断は人間が持つべきです。境界を決めておくと、速さと安全性を両立できます。

実務では、次の順番が扱いやすいです。まず読み取り専用scriptを作る。次にreportを作る。次にdry-run付きで更新候補を出す。最後に、対象件数の上限と承認flagを付けて限定的に書き込む。Webhookやschedule実行は、その後で構いません。

ClaudeCodeLabでは、チーム向けにCLAUDE.md、権限設計、GitHub Actions、review gate、Webhook運用まで含めた導入支援をしています。自社repositoryに合わせた安全な自動化ルールを作りたい場合は、Claude Code研修・導入相談から相談してください。個人でまず型を増やしたい場合は、無料チートシートテンプレート教材から始められます。

まとめ

GitHub APIとClaude Codeを組み合わせると、repository運用の面倒な確認作業をかなり減らせます。ただし、token、権限、pagination、rate limit、Webhook署名、idempotencyを飛ばすと、便利な自動化がそのまま事故の入口になります。最初は読み取り専用、次にdry-run、最後に限定書き込み。この順番を守るのが一番現実的です。

Masaとしてこの記事の内容を検証した結果、最小読み取りscript、pagination helper、Webhook署名検証サーバーはいずれもNode.jsの構文として通る形で整理できました。実際のリポジトリ運用では、いきなりIssueを閉じるのではなく、daily reportで対象を見える化してからlabel付けに進めるほうが、チームの納得も得やすく安全でした。

#Claude Code #GitHub API #自動化 #CI/CD #開発効率化
無料

無料PDF: Claude Code はじめてのチートシート

まずは無料PDFで基本コマンドと最初の使い方をまとめて確認してください。登録後はそのままテンプレート集や導入相談にも進めます。

スパムは送りません。登録情報は厳重に管理します。

Claude Codeを仕事で使える形にしませんか?

無料PDFで基礎を固めたあと、すぐ使えるテンプレート集で試し、必要なら業務自動化や導入相談まで進められます。

Masa

この記事を書いた人

Masa

Claude Codeの実務活用、導入設計、収益導線改善を検証しているエンジニア。10言語の技術メディアを運営中。

PR

関連書籍・参考図書

この記事のテーマに関連する書籍を楽天ブックスで探せます。

※ 当サイトは楽天市場のアフィリエイトプログラムに参加しています。上記リンクから商品をご購入いただくと、運営者に紹介料が支払われる場合があります。