> ## Documentation Index
> Fetch the complete documentation index at: https://docs.auto.sh/llms.txt
> Use this file to discover all available pages before exploring further.

# GitHub Tools

> The brokered GitHub MCP server: the curated tool catalog, how capability scoping derives from mounts, the credential model, and the guardrails on write tools.

Agents reach the GitHub API through a brokered MCP server that auto proxies in front of a pinned deployment of GitHub's own `github-mcp-server`. The agent sees familiar tools — `create_pull_request`, `issue_write`, `get_file_contents` — under the `github` alias, but every call is authorized, scoped, and attributed by the proxy, and GitHub credentials never enter the sandbox. This page covers the catalog, how the allowlist is computed, and the capability gates. Read it with the [mounts reference](/reference/mounts), which owns the declaration syntax.

## How access is provisioned

GitHub access is **mount-backed**. A session gets the `github` MCP server if and only if its agent has at least one `kind: git` mount with `auth: { kind: githubApp }` — the mounts are what the proxy mints tokens from. A `kind: github` tool declaration only tunes the tool list on top of that:

```yaml .auto/agents/pr-review.yaml theme={null}
mounts:
  - kind: git
    repository: acme/platform
    mountPath: /workspace/platform
    auth:
      kind: githubApp
tools:
  github:
    kind: github          # optional; omit to get the curated default allowlist
```

* Mounts, no `github` tool: the server is wired anyway with the default allowlist.
* `github` tool, no githubApp mount: the tool is a silent no-op — no server is wired, because every call would be rejected.
* The alias defaults to `github`, so tools appear to the model as `mcp__github__create_pull_request` and the other names models already know.

## The credential model

The sandbox holds only a session-scoped bearer token for the proxy endpoint. Per request, the proxy:

1. Resolves the session's githubApp mounts and rejects sessions that have none.
2. Determines the target repository from the tool call's arguments. The repo must be a mount or a repository the project is authorized to use — anything else is rejected with `repository is not authorized for this project`.
3. Mints a **fresh GitHub App installation token, down-scoped at mint time** to the target repository and to the permission set derived from the mount's capabilities.
4. Forwards the single `tools/call` upstream with the tool allowlist enforced. The `ghs_` token exists only between the proxy and the upstream server; it is never returned to the sandbox.

The upstream is pinned (`github-mcp-server` v1.3.0) running the `repos`, `issues`, `pull_requests`, and `actions` toolsets, so the catalog below is stable rather than tracking upstream releases silently.

## The catalog

An agent's `kind: github` tool may select from 49 names: 41 upstream tools plus 8 implemented by the proxy itself.

**Upstream (41):** `actions_get`, `actions_list`, `actions_run_trigger`, `add_comment_to_pending_review`, `add_issue_comment`, `add_reply_to_pull_request_comment`, `create_branch`, `create_or_update_file`, `create_pull_request`, `create_repository`, `delete_file`, `fork_repository`, `get_commit`, `get_file_contents`, `get_job_logs`, `get_label`, `get_latest_release`, `get_release_by_tag`, `get_tag`, `issue_read`, `issue_write`, `list_branches`, `list_commits`, `list_issue_types`, `list_issues`, `list_pull_requests`, `list_releases`, `list_repository_collaborators`, `list_tags`, `merge_pull_request`, `pull_request_read`, `pull_request_review_write`, `push_files`, `search_code`, `search_commits`, `search_issues`, `search_pull_requests`, `search_repositories`, `sub_issue_write`, `update_pull_request`, `update_pull_request_branch`.

**Proxy-implemented (8):** executed by auto itself with the same per-call minted token, injected into `tools/list`, never forwarded upstream:

| Tool                             | Purpose                                                                                                                                     |
| -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
| `upsert_issue_comment`           | Create-or-update a marker-keyed issue/PR comment (the idiomatic way to keep one live status comment).                                       |
| `delete_issue_comment`           | Delete an issue/PR comment.                                                                                                                 |
| `download_comment_attachment`    | Fetch a user-uploaded attachment from a comment into a signed URL.                                                                          |
| `enable_pull_request_auto_merge` | Enable auto-merge on a PR (merge-gated, below).                                                                                             |
| `actions_secret_list`            | List Actions secret names and timestamps (secrets-gated; GitHub never returns values).                                                      |
| `actions_secret_write`           | Create/update an Actions secret (secrets-gated).                                                                                            |
| `rerun_failed_jobs`              | Re-run only failed jobs for a workflow run (explicit-tool and `actions: write` gated).                                                      |
| `rerun_failed_jobs_for_pr_head`  | Re-run failed jobs only after the live PR head and failed run attempt match caller expectations (explicit-tool and `actions: write` gated). |

The proxy also extends `pull_request_read` with a labels sub-operation.

### The default allowlist

Omitting `tools:` applies a curated 24-tool default: PR flow (`create_pull_request`, `pull_request_read`, `update_pull_request`, `update_pull_request_branch`, `pull_request_review_write`, `add_comment_to_pending_review`, `add_reply_to_pull_request_comment`), issue/comment flow (`add_issue_comment`, `delete_issue_comment`, `upsert_issue_comment`, `download_comment_attachment`, `issue_read`, `issue_write`), search (`search_pull_requests`, `search_issues`, `search_code`), repo content (`get_file_contents`, `list_commits`, `create_branch`, `create_or_update_file`, `push_files`), and Actions reads (`actions_get`, `actions_list`, `get_job_logs`).

Deliberately excluded by default — name them explicitly to opt in: `create_repository`, `fork_repository`, `delete_file`, `actions_run_trigger`, `rerun_failed_jobs`, `rerun_failed_jobs_for_pr_head`, `list_repository_collaborators`, `sub_issue_write`, release/tag/label reads, `search_repositories`, `search_commits`. Merge and secrets tools work differently: they follow the mount capability, not the tool list — a mount granting `merge: write` or `secrets:` adds them on top of the curated default automatically, and without the grant they stay invisible even when named explicitly (merging with no human in the loop is an opt-in, not a default).

Naming `tools:` replaces the default entirely — the effective list is exactly the names you give, still subject to the capability gates below.

## Capability scoping from mounts

Each githubApp mount declares capability levels that become both the minted token's GitHub permissions and the proxy's gates:

| Capability     | Default | Token permission    |
| -------------- | ------- | ------------------- |
| `contents`     | `write` | `contents`          |
| `pullRequests` | `write` | `pull_requests`     |
| `issues`       | `write` | `issues`            |
| `checks`       | `read`  | `checks`            |
| `actions`      | `read`  | `actions`           |
| `workflows`    | `none`  | `workflows`         |
| `secrets`      | `none`  | `secrets`           |
| `merge`        | `none`  | *(proxy gate only)* |

Every minted token also carries `metadata: read`. `merge: write` requires `contents: write` and `pullRequests: write` on the same mount — GitHub has no standalone merge permission, so an incoherent grant is rejected at apply time.

Two tool families are **double-gated** — invisible and uncallable unless granted, even when an agent names them explicitly:

* **Merge tools** (`merge_pull_request`, `enable_pull_request_auto_merge`): visible only when *some* mount grants `merge: write`; per call, the **target repository's own mount** must grant it. A merge-granted session still cannot merge on its other, non-granted repositories.
* **Secrets tools**: `actions_secret_list` needs `secrets: read` or better, `actions_secret_write` needs `secrets: write` — same two-level model, session visibility from the maximum across mounts, per-repo re-check against the called repo's mount.

The failed-jobs rerun tools are more restrictive because rerunning production work is an operational mutation. The immutable session tool snapshot must name the chosen operation explicitly, the immutable mount snapshot for the target repository must grant `actions: write`, and the currently applied target mount must still grant `actions: write` when the token is minted. A later capability grant does not add the mutation to an already-running session, a later revocation stops the call, and broader bearer-token scopes cannot bypass either snapshot. Agents should use a rerun tool only with explicit human authority for the workflow run being rerun.

`rerun_failed_jobs_for_pr_head` adds a point-of-use fence: immediately before the mutation, the proxy requires the PR to be open, both the live PR and run heads to match the expected full SHA, the run's current attempt to match the expected attempt, and that attempt to be completed with conclusion `failure`. GitHub's failed-jobs rerun endpoint has no conditional head or attempt parameter, so the live reads and POST are **not provider-atomic**. The residual race can rerun only the old workflow; downstream orchestration must still verify the current PR head and evidence cycle and must never count a stale rerun as current evidence.

A called repository that is itself a mount uses that mount's capabilities. A project-authorized repository that is *not* a mount gets the standard default capability set, with merge, secrets, and explicitly gated Actions mutations always denied.

```yaml theme={null}
mounts:
  - kind: git
    repository: acme/platform
    mountPath: /workspace/platform
    auth:
      kind: githubApp
      capabilities:
        merge: write        # opt in to merge tools on this repo
        contents: write
        pullRequests: write
```

## Guardrails on write tools

Beyond scoping, the proxy enforces several invariants:

* **Visible attribution**: covered write-tool bodies are stamped with an agent-attribution header derived from the session identity (never from tool arguments), linking back to the session page when project slugs resolve.
* **Artifact attribution**: successful write-tool calls are recorded as artifact attributions — this is how a PR an agent opens auto-binds to its session on `pull_request.opened` (see [triggers](/reference/triggers) and [sessions](/concepts/sessions)).
* **Commit-author verification at PR creation**, so attributed authorship on PRs matches the platform's records.
* **Comment substance guard**: `add_issue_comment` rejects near-empty bodies before anything is minted or forwarded, so a stray placeholder can never land on an issue.
* **One `tools/call` per request**: batched calls are rejected outright rather than partially attributed.
* A tool outside the session's allowlist fails with a tool-level error (`unknown tool "…": not enabled for this session`), which agents recover from far better than a transport error.

## Example: a review agent's GitHub surface

A PR-review agent that comments and manages checks but can never merge or push:

```yaml .auto/agents/pr-review.yaml theme={null}
name: pr-review
mounts:
  - kind: git
    repository: acme/platform
    mountPath: /workspace/platform
    ref: refs/pull/{{payload.github.pullRequest.number}}/head
    auth:
      kind: githubApp
      capabilities:
        contents: read
        pullRequests: write
        issues: write
tools:
  github:
    kind: github
    tools:
      - pull_request_read
      - pull_request_review_write
      - add_comment_to_pending_review
      - add_reply_to_pull_request_comment
      - upsert_issue_comment
      - get_file_contents
      - list_commits
      - search_code
```

With `contents: read`, the minted tokens cannot push even through git; with no `merge` capability, merge tools do not exist for this session regardless of what any prompt asks for.
