# Connect GitHub to Prodpeek Profile: `github/read-only` · connection kind: `http` · tier 2 Credential: A fine-grained personal access token Suggested URL: `https://api.githubcopilot.com/mcp/` One connection that reads GitHub: repositories, code, commits, pull requests, issues, releases and Actions — including the job logs that say why the deploy failed. The narrowing that matters is the token's repository scope. ## What you must not do You cannot create this credential on the user's behalf — it needs their login and, usually, an approval step. Walk them through it and verify the result. Never ask them to paste the credential into the chat; it goes straight into Prodpeek's console, which encrypts it at rest. There is one GitHub profile now, not three. Do not ask the user which part of GitHub they need — they do not know yet, and that was the problem with splitting it. Ask instead which REPOSITORIES the connection should reach, and tell them to scope the fine-grained token to exactly those. ## Grant exactly these permissions - `A fine-grained token, scoped to only the repositories this connection should reach` — This is the fence that does the work. Fine-grained tokens name their repositories, so a token for ejd-web cannot see ejd-billing no matter what the gateway does or does not allow. - `Every permission left on Read-only` — Contents, Metadata, Pull requests, Issues and Actions on **Read-only**. That makes every write in this profile refused at GitHub as well as here — two fences, which is the whole point. - `An expiry` — Fine-grained tokens expire by default and you can extend that to a year. Take the shorter one; you will rotate it on a schedule you choose rather than one an incident chooses for you. ## Refuse these, and say why if the user asks for them - `Any permission set to Read and write` — The gateway denies every write regardless, but a token that could merge a pull request means the two fences disagree, and only one of them is in your control if the other has a bug. - `Secret scanning alerts: Read` — A secret scanning alert contains THE LEAKED SECRET ITSELF — that is what the feature is for. The profile denies those tools, and the token should not be able to reach them either. This is the single most important box to leave unticked. - `Code scanning alerts: Read` — An alert quotes the vulnerable code and describes how to reach it. That is an exploitation map for a system you run. - `Administration, or a classic token with `repo`` — `repo` is one scope that means read, write, delete, settings and webhooks across every repository the user can see. It is the opposite of scoping. - `A token made by a human account that can see everything` — It carries that person's whole access, disappears when they leave, and attributes every call to them rather than to the integration. If the organisation allows it, make a machine user. ## Verify before the credential is used - The token page lists exactly the repositories you intended, not 'All repositories'. - Every permission on the token reads Read-only. - Secret scanning alerts and Code scanning alerts are set to No access. - Test connection shows get_job_logs under Allowed and get_secret_scanning_alert under Blocked. ## Then, in Prodpeek 1. Services → Add a service → choose the profile `github/read-only`. 2. Connection kind `http`, URL `https://api.githubcopilot.com/mcp/`. 3. Paste the credential. It is encrypted in the store and never shown again. 4. Run **Test connection**. It lists every tool the upstream advertises and how the profile classifies each one. Anything under "not in the policy" is denied by default — report that list rather than assuming it is fine. ## Full human walkthrough ## Why there is one profile and not three There used to be `github/actions-read`, `github/code-read` and `github/triage-read`. Choosing between them meant deciding, at connection time, which third of GitHub an agent would ever need — and during an incident the honest answer is "I do not know yet". Someone debugging a failed deploy needs the workflow run, the job log that says what broke, the commit that triggered it, the file that commit changed and the pull request it arrived in. That is all three profiles. Expressing it as three connections meant three tokens, three audit trails and three chances to bind the wrong one. So there is one connection that reads GitHub. **The narrowing that matters is the token**, and it is a better fence than the split ever was: a fine-grained token names its repositories, and the gateway cannot widen that. ## Step 1 — mint the token 1. **Settings → Developer settings → Personal access tokens → Fine-grained tokens → Generate new token**. 2. **Resource owner**: the organisation, not your personal account, if the repositories belong to one. 3. **Repository access → Only select repositories**, and pick exactly the ones this connection should reach. 4. **Permissions**, all on **Read-only**: | Permission | Why | |---|---| | Contents | files, commits, branches, releases | | Metadata | required; repository basics | | Pull requests | PRs, files, reviews, status | | Issues | issues and comments | | Actions | workflow runs, jobs and logs | 5. Leave **Secret scanning alerts** and **Code scanning alerts** at **No access**. 6. Set an expiry. Generate. Copy it. ## Step 2 — connect it **Services → Add a service**, pick `github/read-only`. The upstream is GitHub's hosted MCP server and is filled in for you: | Field | Value | |---|---| | Upstream | `https://api.githubcopilot.com/mcp/` | | Credential | the fine-grained token | Name it for what it reaches — `ejd-github` — because the name is the namespace an agent sees. ## The two judgement calls worth knowing about **Job logs are allowed.** `get_job_logs` is classified `read_leaks`, honestly: a build log contains whatever the build printed, and builds print more than they should. It is allowed anyway, because "why did the deploy fail" is unanswerable without it — and a denied log just means somebody opens the GitHub UI and reads the same thing unaudited. The leak is stated rather than hidden, and [principle 13](../reference/egress.md) keeps that output away from an AI. **Secret scanning alerts are denied, and always will be.** That endpoint returns the leaked credential itself. There is no scoping that makes handing an agent your committed secrets acceptable, so it is not a setting. Artifact downloads are denied too: an artifact is an arbitrary blob — a built image, a tarball of the workspace — and that is a download, not a diagnostic. `list_workflow_run_artifacts` says what exists and `get_job_logs` says what happened. ## Checking the token really cannot write ```bash # Should succeed: reading curl -sS -H "Authorization: Bearer $GH_TOKEN" \ https://api.github.com/repos/OWNER/REPO/actions/runs?per_page=1 | head -c 200 # Should FAIL with 403: creating an issue curl -sS -o /dev/null -w '%{http_code}\n' -X POST \ -H "Authorization: Bearer $GH_TOKEN" \ -d '{"title":"prodpeek-permission-test"}' \ https://api.github.com/repos/OWNER/REPO/issues # Should FAIL with 403: secret scanning curl -sS -o /dev/null -w '%{http_code}\n' \ -H "Authorization: Bearer $GH_TOKEN" \ https://api.github.com/repos/OWNER/REPO/secret-scanning/alerts ``` Two `403`s and a `200` is what a correctly minted token looks like.