Skip to main content
139 toolsOAuth bearer
github_example.py
Most of the REST API an agent working in a repository reaches for: issues and their labels, assignees, milestones and history; pull requests down to a comment on one line of a diff; CI, from listing runs to reading the failing job’s log; the Git object store, which is how a change across six files becomes one commit rather than six; releases; repositories, commits, file contents and search; and the notification and security-alert feeds an always-on agent wakes up on. Two boundaries worth knowing before you look for them. Projects v2 and resolving a review thread are GraphQL-only — there is no REST equivalent, so neither is here. And the Actions secrets write endpoints are not carried: they take a libsodium-sealed box, which is a dependency this library does not have. actions_list_repo_secrets reads the names, which is the part an agent debugging a workflow actually needs, and it is an endpoint that cannot return a value.

Authenticating

This pack takes a GitHub OAuth bearer token, and configure() is optional when $GITHUB_TOKEN is set. Which credential provider you hand it depends on whose account the calls run as.
Refer to GitHub’s provider page for the “GITHUB” constant the snippets below name, the scopes these 139 tools ask for, and this server’s refresh behaviour.

A token you hold

For a script, or a notebook. EnvTokenProvider re-reads the variable on every call, so a token rotated beside the process is picked up without a restart; StaticTokenProvider takes one you already hold as a string. Neither renews anything, so the calls stop when the token expires.
github_script.py

One account, refreshed

For an agent or a server acting as you. OAuth2Client turns a client registration and a stored refresh token into an access token, and renews it before it lapses.
github_agent.py
No refresh token yet? Getting the first grant is the one-time consent flow that hands you one.

Many end users

For a product whose users each connect their own account. SubjectProvider builds one credential per user through a factory you write, and use_subject names the user a call acts for. Your users’ accounts is the consent route inside your app; serving many users is the per-subject cache and its eviction.
github_server.py
A classic PAT, a fine-grained token and a GitHub App installation token all work. The declared scopes are what a classic PAT needs; a fine-grained token instead wants read/write on Issues, Pull requests, Contents and Metadata.

The client

oauth_tool_factory is the whole client: a thin wrapper over httpx that attaches your token and these endpoint constants to each request. PyGithub does not enter your dependency tree.
credentials is whichever of the three you built in Authenticating. A pack takes it through configure(); a client you build takes the same object as credential_provider, and has no configure() of its own.

Paging through a list

A cursor belongs to the tool that returns it, so it is declared on that tool’s builder call:
github_pagination.py
Pagination is declared on issues_list_for_repo, issues_list_comments, pulls_list, pulls_list_files, repos_list_for_authenticated_user, repos_list_commits, pulls_list_reviews, branches_list, actions_list_workflow_runs, search_code, actions_list_jobs_for_run, actions_list_workflows, actions_list_runs_for_workflow, actions_list_run_artifacts, actions_list_repo_secrets, checks_list_for_ref, checks_list_annotations, checks_list_suites_for_ref, repos_get_combined_status, pulls_list_review_comments, pulls_list_review_comments_for_repo, pulls_list_comments_for_review, pulls_list_commits, repos_get_commit, repos_compare_commits, repos_list_tags, issues_list_labels_for_repo, issues_list_milestones, issues_list_timeline, issues_list_events, issues_list_for_authenticated_user, issues_list_sub_issues, releases_list, releases_list_assets, notifications_list, code_scanning_list_alerts, secret_scanning_list_alerts, repos_list_for_org, repos_list_contributors, search_issues, search_repositories, search_commits and search_users. The other 96 take no cursor.

Tools

Each is a Tool, called with ainvoke as in the snippet above. The name links to its parameters, its response and what it costs.
Issuesissues_list_for_repoGETList issues in a repository.issues_getGETGet a single issue by its number, including the full body text.issues_createPOSTOpen an issue.issues_updatePATCHUpdate an issue.issues_create_commentPOSTPost a comment on an issue or pull request.issues_list_commentsGETList the comments on an issue or pull request, oldest first.issues_list_labels_for_repoGETList the labels a repository defines, with their colours and descriptions.issues_add_labelsPOSTAdd labels to an issue, keeping the ones already on it.issues_set_labelsPUTReplace an issue’s labels with exactly this set, dropping any others.issues_remove_labelDELETETake one label off an issue, leaving the others in place.issues_create_labelPOSTDefine a new label on the repository.issues_update_labelPATCHRename a label or change its colour, description, or archived state.issues_delete_labelDELETEDelete a label, removing it from every issue that carries it.issues_add_assigneesPOSTAssign people to an issue, keeping whoever is already assigned.issues_remove_assigneesDELETEUnassign people from an issue.issues_list_milestonesGETList a repository’s milestones, with how many issues in each are open and closed.issues_create_milestonePOSTCreate a milestone.issues_update_milestonePATCHUpdate a milestone.issues_get_commentGETGet one issue comment by its id — the id from issues_list_comments, not the issue number.issues_update_commentPATCHEdit an issue comment’s text.issues_delete_commentDELETEDelete an issue comment.issues_list_timelineGETList everything that has happened to an issue in order — comments, labels, assignments, cross-references, the commits that mentioned it.issues_list_eventsGETList an issue’s state changes — labelled, assigned, closed, renamed — without the comments.issues_lockPUTLock an issue or pull request’s conversation.issues_unlockDELETEUnlock a conversation that was locked.issues_list_for_authenticated_userGETList the authenticated user’s issues across every repository they can see — the agent’s own queue.issues_list_sub_issuesGETList the sub-issues of an issue — how a tracking issue’s pieces are found.issues_add_sub_issuePOSTAttach an existing issue to this one as a sub-issue.Pullspulls_listGETList pull requests.pulls_getGETGet a single pull request, including its mergeability and the counts of changed files, additions and deletions.pulls_createPOSTOpen a pull request from head into base.pulls_list_filesGETList the files a pull request changes, with per-file additions, deletions and patch text.pulls_updatePATCHUpdate a pull request’s title, body, state or base branch.pulls_mergePUTMerge a pull request.pulls_create_reviewPOSTApprove a pull request, request changes on it, or leave a comment.pulls_list_reviewsGETList the reviews left on a pull request, oldest first.pulls_request_reviewersPOSTAsk users or teams to review a pull request.pulls_create_review_commentPOSTLeave a comment on a specific line of a pull request’s diff — the thing reviewing is made of.pulls_list_review_commentsGETList the line comments on a pull request, each with the file and line it sits on.pulls_reply_to_review_commentPOSTReply to an existing review comment, continuing its thread.pulls_update_review_commentPATCHEdit the text of a review comment.pulls_delete_review_commentDELETEDelete a review comment.pulls_list_review_comments_for_repoGETList the line comments across every pull request in a repository.pulls_get_reviewGETGet one review of a pull request, with its state and summary text.pulls_submit_reviewPOSTSubmit a pending review as an approval, a request for changes, or a comment.pulls_update_reviewPUTEdit a review’s summary text.pulls_dismiss_reviewPUTDismiss a review so it stops blocking the pull request, with a message saying why.pulls_delete_pending_reviewDELETEDiscard a review that was never submitted.pulls_list_comments_for_reviewGETList the line comments that belong to one review, rather than to the pull request as a whole.pulls_get_diffGETGet a pull request’s whole change as a unified diff, in one call.pulls_list_commitsGETList the commits on a pull request.pulls_update_branchPUTBring a pull request’s branch up to date by merging the base branch into it — the fix for a ‘this branch is out of date’ block.pulls_check_mergedGETAsk whether a pull request has been merged.pulls_list_requested_reviewersGETList who has been asked to review and has not answered yet.pulls_remove_requested_reviewersDELETEWithdraw a review request from users or teams.Reposrepos_getGETGet a repository’s metadata, including its default branch and topics.repos_list_for_authenticated_userGETList repositories the authenticated user can access.repos_get_contentGETRead a file, or list a directory.repos_list_commitsGETList commits, newest first.repos_create_or_update_filePUTCreate a file or replace an existing one, in a single commit.branches_listGETList a repository’s branches and the commit each one points at.git_refs_createPOSTCreate a branch or tag pointing at an existing commit.repos_get_commitGETGet one commit with its diff and per-file line counts.repos_compare_commitsGETCompare two refs and get what is in one and not the other: the status, how far ahead or behind, the commits and the changed files.repos_delete_fileDELETEDelete a file in a commit of its own.repos_merge_branchesPOSTMerge one branch into another directly, without opening a pull request.repos_get_branchGETGet one branch: its head commit and whether it is protected.repos_rename_branchPOSTRename a branch.repos_get_branch_protectionGETRead a branch’s protection rules — required checks, required reviews, linear history — before attempting a write that they would refuse.repos_list_branches_for_head_commitGETList the branches whose head is this exact commit — ‘has this landed, and where’, asked from the commit’s side.repos_get_readmeGETGet the repository’s README, decoded to text.repos_list_tagsGETList a repository’s tags with the commit each points at, newest first.repos_create_forkPOSTFork a repository, optionally into an organization or under a new name.repos_list_for_orgGETList an organization’s repositories.repos_list_languagesGETList a repository’s languages with bytes of code each — the quickest way to find out what a repository is written in before reading any of it.repos_list_contributorsGETList contributors, most commits first — who to ask about a repository.Searchsearch_issuesGETSearch issues and pull requests across GitHub with qualifiers, e.g. ‘repo:owner/name is:issue is:open label:bug’.search_repositoriesGETSearch repositories with qualifiers, e.g. ‘topic:cli language:go stars:>500’.search_codeGETSearch code across GitHub.search_commitsGETSearch commits by message, author or date across GitHub — ‘repo:owner/name fix flaky test’.search_usersGETSearch users and organizations — ‘type:org language:rust’.Usersusers_get_authenticatedGETGet the authenticated user.Actionsactions_list_workflow_runsGETList CI runs, most recent first.actions_rerun_workflowPOSTRun a workflow again from the start, including jobs that passed.actions_cancel_workflow_runPOSTAsk GitHub to cancel a running workflow.actions_get_workflow_runGETGet one workflow run: its status, conclusion, branch, commit and attempt number.actions_list_jobs_for_runGETList a run’s jobs, each with its conclusion and its failing steps named.actions_get_jobGETGet one job of a workflow run, with the steps that did not pass named individually and the runner it ran on.actions_download_job_logsGETRead a failing job’s log.actions_download_run_logsGETRead a whole run’s logs in one call.actions_rerun_failed_jobsPOSTRe-run only the failed jobs of a run, and the jobs that depend on them.actions_list_workflowsGETList the repository’s workflows, with the file each one is defined in and whether it is active or disabled.actions_get_workflowGETGet one workflow.actions_create_workflow_dispatchPOSTTrigger a workflow by hand on a branch or tag.actions_list_runs_for_workflowGETList the runs of one workflow, most recent first — ‘is this pipeline green’, rather than ‘is anything red’.actions_list_run_artifactsGETList what a run uploaded, with each artifact’s size and whether it has expired.actions_get_artifactGETGet one artifact’s name, size, expiry and digest.actions_download_artifactGETDownload an artifact and report what is in it: every entry’s name and size, plus the contents of the small text files — a JUnit report or a coverage summary comes back readable.actions_get_run_usageGETGet a run’s total run time and its billable milliseconds per runner operating system.actions_list_pending_deploymentsGETList the environments a run is waiting on for approval, and who can approve them.actions_review_pending_deploymentsPOSTApprove or reject a run’s pending deployments.actions_list_repo_secretsGETList the names of the repository’s Actions secrets, with when each was created and last changed.Checkschecks_list_for_refGETList the check runs reported against a commit, branch or tag — every CI provider that reports as a GitHub App, Actions included.checks_get_runGETGet one check run, with its summary text and how many annotations it produced.checks_list_annotationsGETList a check run’s annotations: each failure with the file, the line range, a level and a message, already extracted by whoever ran the check.checks_list_suites_for_refGETList the check suites for a commit, branch or tag — one suite per app that reported.repos_get_combined_statusGETGet the single rolled-up state of a commit — ‘success’, ‘failure’, ‘pending’ or ‘error’ — plus one line per reporting context.Gitgit_refs_getGETGet a reference and the commit SHA it points at — how you read a branch’s current head before building on it.git_refs_updatePATCHPoint a branch or tag at a different commit — the step that makes a commit built with git_trees_create and git_commits_create visible.git_refs_deleteDELETEDelete a branch or tag, by reference.git_refs_list_matchingGETList every reference under a prefix — heads/ for all branches, tags/v2 for the v2 tags.git_blobs_createPOSTWrite a file’s content into the object store and get its SHA back, without committing anything.git_blobs_getGETRead a blob by SHA, decoded to text.git_trees_createPOSTBuild one commit’s worth of changes across any number of files.git_trees_getGETRead a tree: every path in it with its mode and object SHA.git_commits_createPOSTCreate a commit pointing at a tree.git_commits_getGETRead a commit object: its message, its tree SHA and its parents.git_tags_createPOSTCreate an annotated tag object.Releasesreleases_listGETList a repository’s releases, newest first.releases_getGETGet one release by its numeric id, with its notes and its assets.releases_get_latestGETGet the latest published release — GitHub’s definition, meaning the most recent release that is neither a draft nor a prerelease.releases_get_by_tagGETGet a release by its tag name, such as ‘v1.4.0’.releases_createPOSTPublish a release, creating its tag if it does not exist.releases_updatePATCHUpdate a release; anything omitted is left unchanged.releases_deleteDELETEDelete a release.releases_generate_notesPOSTGenerate release-note text from the pull requests merged since a previous tag.releases_list_assetsGETList the files attached to a release, with sizes and download counts.releases_upload_assetPOSTAttach a file to a release.Monitoringnotifications_listGETList your unread notifications — mentions, review requests, assignments — most recently updated first.notifications_mark_readPUTMark notifications as read — all of them, or everything up to last_read_at.notifications_get_threadGETGet one notification thread: what it concerns, and the reason you were notified.notifications_mark_thread_readPATCHMark one notification thread as read — how an agent takes a handled item off its own queue.dependabot_list_alertsGETList a repository’s Dependabot alerts: which dependency, how severe, and the first version that fixes it.dependabot_get_alertGETGet one Dependabot alert, with its advisory summary and the patched version.dependabot_update_alertPATCHDismiss a Dependabot alert or reopen one.code_scanning_list_alertsGETList code scanning alerts, each with the rule that fired and the file and line it fired on.code_scanning_get_alertGETGet one code scanning alert, with the rule’s description and the most recent place it was seen.secret_scanning_list_alertsGETList secret scanning alerts: which pattern matched, which provider, and whether the credential is still valid.rate_limit_getGETCheck how much API budget is left, per resource.

Constant headers beside a bearer token

GitHub wants three headers on every request that have nothing to do with the model: an Accept naming the media type, an X-GitHub-Api-Version, and a User-Agent — GitHub answers 403 to a request without one. They are declared once as static_headers on the factory and sent verbatim. None of them appears in any tool’s LLM schema, so they cost nothing in context and a model has no path to setting them. This is the OAuth-plus-constant-headers shape that static_headers exists for. Pinning the version is the point of the exercise: an unpinned integration is one that breaks on GitHub’s schedule rather than on yours. github.API_VERSION is the version these schemas were written against.

Paging by number

There is no cursor. You ask for page 1, 2, 3 and stop when a page comes back shorter than per_page, which is why both parameters are required on a page-number Pagination — without the page size there is no way to tell a full page from the last one. Both are real fields on the schemas that declare it.
github_page_through_issues.py
The search endpoints wrap their results in items rather than returning a bare array, so they declare the same style with items_field="items". That is the only difference between GITHUB_PAGINATION and SEARCH_PAGINATION.

Response trimming

One issue is 4–6KB of nested user objects, reaction counts and twenty *_url fields; a page of thirty is a quarter of a million characters, almost none of it actionable. Twenty-four of the twenty-nine tools carry a response handler. The handlers project rather than filter — they narrow each object but never drop one, because dropping an item would corrupt the page-length signal that page-number pagination depends on. issues_create_comment, issues_list_comments and pulls_list_files have no handler and return GitHub’s payload as it arrived. pulls_list_files in particular carries full patch text, which is the point of calling it and also the largest response in the pack.

Gotchas

That is GitHub’s model, not a quirk of this pack: issues_list_for_repo returns both. GitHub’s own signal for it is the presence of a pull_request key, which does not survive trimming, so the handler sets is_pull_request on the ones that are.
30 requests per minute, against 5,000 per hour for the rest of the REST API. A search-heavy agent hits that long before it hits the general limit. When it does, APIError.retry_after carries GitHub’s own answer to “when?” — see the wire contract.
labels and assignees on issues_update overwrite what is there. To add one label, read the current set with issues_get and send the union. Closing an issue is state="closed", with state_reason saying why.
Passing type together with visibility or affiliation is a 422 from GitHub. Use one or the other; a request carrying both is refused locally, before the round trip.All three carry a documented default, and none of them is set on the schema. That is deliberate. GitHub’s defaults describe what it applies to a request that omits the parameter, so copying them onto the fields put all three on the wire for every call and made the 422 the normal case rather than the edge one. A default you send is not a default.
Pass a file path and it returns that file with its contents decoded as text; pass a directory path and it returns a listing. Pass an empty path for the repository root. The shape of the response depends on what the path pointed at, which is GitHub’s design.
A binary file and a file over 1MB both come back as a content string saying which of the two happened, plus the download_url to fetch it with. Neither is an error, and both are silent if you do not look for them: a binary file base64-decodes to replacement characters, which reads as content until you try to use it, and GitHub answers a file between 1MB and 100MB with encoding: "none" and an empty string. Over 100MB it does not serve this endpoint at all.
GitHub serves only the first 1,000 matches of any query. total_count reports the real size of the match set, so a page-number walk has every reason to keep going and gets a 422 when page × per_page passes 1,000. The search schemas check that pair locally and say to narrow the query instead — a date range, a repository, a label.