Skip to content

feat(github): gh reaches every installed org, not just the primary (v0.438.0) - #812

Merged
vikasprogrammer merged 1 commit into
mainfrom
feat/gh-org-token
Sep 10, 2026
Merged

feat(github): gh reaches every installed org, not just the primary (v0.438.0)#812
vikasprogrammer merged 1 commit into
mainfrom
feat/gh-org-token

Conversation

@vikasprogrammer

Copy link
Copy Markdown
Owner

Phase 4 of docs/github-multi-org-plan.md, after #800 (plan) and #807 (phases 1–3).

The gap

v0.435.0 gave plain git a per-repo credential helper, so clone/fetch/push reach every org the App is installed on. gh doesn't — it reads GH_TOKEN and ignores git credential helpers entirely, so gh pr create, gh issue and gh api stayed pinned to whichever org the ambient token covers. The failure there is a 404, which reads as "no such repository" rather than "wrong credential".

The tool

github_token({ org }) returns that org's bot token for the agent to export, over the same session-secret loopback route (POST /api/agent/github/credential) the credential helper uses — no new route, no new class of credential.

  • It says plainly that the export replaces the ambient token for the rest of the shell, and suggests a subshell if the agent is going back to the primary org. That's the sharp edge of handing a token to a shell, so it's stated rather than left to be discovered.
  • not_installed is a typed, non-retryable refusal that names the orgs which do work — an agent asks for an install instead of looping on a 404.
  • It points back at git for anything git can do, so the tool is reserved for genuinely gh-only operations.

Conditional, and the guard

Offered only when GH_ORG_TOKEN=1, set at launch when the App spans several orgs and the run is on the bot lane:

  • a single-org tenant never sees it — no schema cost for a tool they could never use (the MCP tool list is already a large fraction of every prompt);
  • a run acting as a human who linked their own GitHub is never offered it. Their user token already spans every org they can reach, and handing them a bot token would re-author their work as the App bot. That guard now holds in three places — the launch gate, the route, and the credential helper.

Verification

  • npm run typecheck, cd web && npm run build, full npm run test:governance — all clean.
  • scripts/github-multi-org-test.cjs is 61 checks now. The new section drives the real MCP server (spawn → initializetools/listtools/call): offered on a multi-org bot run, absent on a single-org one, absent on a member-identity run, description names the reachable orgs, a call returns the right org's export line with the replace warning, and an uninstalled org is a refusal rather than a token.
  • Also asserts the launch gate directly via buildMcpConfigJson, so "offered but unreachable" and "reachable but never offered" both fail the suite.

Docs: docs/agent-mcp-tools.md gains the tool↔route↔store row (62 always-on + 13 conditional), and CLAUDE.md's GitHub note now records the gh escape hatch instead of saying gh is stuck.

Remaining from the plan: the console installations list + primary picker.

🤖 Generated with Claude Code

https://claude.ai/code/session_01UfJRSqtHhuk7x51Cqy4NB5

…(v0.438.0)

Phase 4 of docs/github-multi-org-plan.md. v0.435.0 gave plain `git` a per-repo
credential helper, but `gh` reads GH_TOKEN and ignores git credential helpers, so
`gh pr create` / `gh issue` / `gh api` stayed pinned to whichever org the ambient
token covered — and failed there with a 404 that reads as "no such repository".

`github_token({ org })` hands back that org's bot token for the agent to export,
over the same session-secret loopback route the helper uses, and says plainly that
the export REPLACES the ambient token for the rest of the shell. `not_installed` is
a typed, non-retryable refusal that names the orgs which do work, so an agent asks
for an install instead of looping.

Conditional by design (GH_ORG_TOKEN=1): offered only when the App spans several
orgs AND the run is on the bot lane. A single-org tenant pays nothing for a schema
it can never use, and a run acting as a human who linked their own GitHub is never
offered it — their user token already spans their orgs, and a bot token would
re-author their work as the bot. That guard now holds in three places: the launch
gate, the route, and the credential helper.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UfJRSqtHhuk7x51Cqy4NB5
@vikasprogrammer
vikasprogrammer merged commit e6891c0 into main Sep 10, 2026
1 check passed
@vikasprogrammer
vikasprogrammer deleted the feat/gh-org-token branch September 10, 2026 12:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant