Repository navigation
Per-person GitHub connect for non-admins (supersedes #2789, TASK-290/334) - #3100
Conversation
Co-authored-by: kai <WyJrYWkiLCJkZWZhdWx0Il0@agents.commonly.invalid> Agent-Model: codex/gpt-6-sol/medium Co-authored-by: rivet <WyJyaXZldCIsImRlZmF1bHQiXQ@agents.commonly.invalid> Agent-Model: claude/claude-opus-5-5/default Co-authored-by: vera <WyJ2ZXJhIiwiZGVmYXVsdCJd@agents.commonly.invalid>
Co-authored-by: kai <WyJrYWkiLCJkZWZhdWx0Il0@agents.commonly.invalid> Agent-Model: codex/gpt-6-sol/medium Co-authored-by: rivet <WyJyaXZldCIsImRlZmF1bHQiXQ@agents.commonly.invalid> Agent-Model: claude/claude-opus-5-5/default Co-authored-by: vera <WyJ2ZXJhIiwiZGVmYXVsdCJd@agents.commonly.invalid>
Co-authored-by: kai <WyJrYWkiLCJkZWZhdWx0Il0@agents.commonly.invalid> Agent-Model: codex/gpt-6-sol/medium Co-authored-by: rivet <WyJyaXZldCIsImRlZmF1bHQiXQ@agents.commonly.invalid> Agent-Model: claude/claude-opus-5-5/default Co-authored-by: vera <WyJ2ZXJhIiwiZGVmYXVsdCJd@agents.commonly.invalid>
Co-authored-by: kai <WyJrYWkiLCJkZWZhdWx0Il0@agents.commonly.invalid> Agent-Model: codex/gpt-6-sol/medium Co-authored-by: rivet <WyJyaXZldCIsImRlZmF1bHQiXQ@agents.commonly.invalid> Agent-Model: claude/claude-opus-5-5/default Co-authored-by: vera <WyJ2ZXJhIiwiZGVmYXVsdCJd@agents.commonly.invalid>
Co-authored-by: kai <WyJrYWkiLCJkZWZhdWx0Il0@agents.commonly.invalid> Agent-Model: codex/gpt-6-sol/medium Co-authored-by: rivet <WyJyaXZldCIsImRlZmF1bHQiXQ@agents.commonly.invalid> Agent-Model: claude/claude-opus-5-5/default Co-authored-by: vera <WyJ2ZXJhIiwiZGVmYXVsdCJd@agents.commonly.invalid>
Co-authored-by: kai <WyJrYWkiLCJkZWZhdWx0Il0@agents.commonly.invalid> Agent-Model: codex/gpt-6-sol/medium Co-authored-by: rivet <WyJyaXZldCIsImRlZmF1bHQiXQ@agents.commonly.invalid> Agent-Model: claude/claude-opus-5-5/default Co-authored-by: vera <WyJ2ZXJhIiwiZGVmYXVsdCJd@agents.commonly.invalid>
…mcp hidden The ops-runner env mirror guard (#2426) failed because the GitHub MCP client refs reached backend-deployment only. Mirror both optional refs. Hide github-mcp in dev until a fresh non-admin account walks connect, grant, call and refusal end to end (TASK-290); that account joins the allowlist. Written by Rivet, a Commonly agent — https://commonly.me/v2/pods/6a8f6dc7a1dccf2e02f31015 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: rivet <WyJyaXZldCIsImRlZmF1bHQiXQ@agents.commonly.invalid> Agent-Model: claude/claude-opus-5-5/default Co-authored-by: vera <WyJ2ZXJhIiwiZGVmYXVsdCJd@agents.commonly.invalid>
…epos Written by Rivet, a Commonly agent — https://commonly.me/v2/pods/6a8f6dc7a1dccf2e02f31015 GitHub's App user token reaches only the repositories the App is installed on. A person who connects github-mcp needs a place to choose those repositories, and today nothing points them there. Hosted entries gain an optional accessPage. It is validated as an absolute https URL when entries are asserted, projected onto the catalogue row, and rendered as "Choose what <label> can reach" on the row, both before Connect and after connecting. GitHub derives the page from GITHUB_MCP_APP_SLUG. That env var comes from hostedMcp.githubMcp.appSlug, is mirrored to the ops runner, and is set to commonly-connect on dev. A slug that fails GitHub's shape check yields no link. TASK-290 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: rivet <WyJyaXZldCIsImRlZmF1bHQiXQ@agents.commonly.invalid> Agent-Model: claude/claude-opus-5-5/default Co-authored-by: vera <WyJ2ZXJhIiwiZGVmYXVsdCJd@agents.commonly.invalid>
Written by Rivet, a Commonly agent — https://commonly.me/v2/pods/6a8f6dc7a1dccf2e02f31015 On main, a successful callback makes three findOneAndUpdate writes, and the commit carrying config.grantedScope is the third. This test read the second, so it failed with a TypeError and never checked the empty-scope rule. It now reads calls[2], like its siblings, and asserts the call count. The test goes red when a returned empty scope is replaced by the requested scopes. Reported by CodeRabbit on #2789. TASK-290 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: rivet <WyJyaXZldCIsImRlZmF1bHQiXQ@agents.commonly.invalid> Agent-Model: claude/claude-opus-5-5/default Co-authored-by: vera <WyJ2ZXJhIiwiZGVmYXVsdCJd@agents.commonly.invalid>
…anting Written by Rivet, a Commonly agent — https://commonly.me/v2/pods/6a8f6dc7a1dccf2e02f31015 Two CodeRabbit findings on 5477958, both confirmed against the code. The access-page check was a prefix regex, which accepts `https://#fragment` and `https://?q`. Neither has a host to open. The entry is now also parsed with URL and must be https with a hostname. A live grant takes its entry off the offer list, so the link to the App install page disappeared once a person granted. It now sits on the grant's details for the granter, who owns the connection. It can't go on the row itself, because the row is a button. The link is one helper shared by both places. Each test fails with its fix mutated away. TASK-290 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: rivet <WyJyaXZldCIsImRlZmF1bHQiXQ@agents.commonly.invalid> Agent-Model: claude/claude-opus-5-5/default Co-authored-by: vera <WyJ2ZXJhIiwiZGVmYXVsdCJd@agents.commonly.invalid>
GitHub refuses a revoked refresh token with HTTP 200 {error: bad_refresh_token}.
That was treated as retryable, so a revoked row stayed connected. It now ends
in connection_error like invalid_grant.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-authored-by: vera <WyJ2ZXJhIiwiZGVmYXVsdCJd@agents.commonly.invalid>
Agent-Model: claude/claude-opus-5-5/default
lilyshen0722
left a comment
There was a problem hiding this comment.
GATE: PASS at 059b675 (Vera, Opus-class subagent review, single D218 round for the rebased #2789 change).
What I ran at 059b675: the entry's backend suites (170 of 170), the wiring test (7 of 7), the credential suite (50 of 50) and helm template against values-dev.yaml. tsc reports nothing in the touched files. A fresh Opus reviewer read the diff against main for auth, hiding, read-only pins, links, helm and vacuous tests.
Clean:
- The server-held
github-appcredential is not reachable. The entry's client comes fromGITHUB_MCP_CLIENT_*, the broker spends only the person's own credential, androutes/integrations.tsis untouched. - Hiding is enforced server-side on the catalogue,
/start(404, no admin bypass) and grant minting. The callback cannot finish without/start's pending state and cookie. - All six pinned schemas match upstream
__toolsnaps__at 796afb7 and arereadOnlyHint: true. - No open redirect: the host is fixed to github.com, the slug is
[a-z0-9-], and the link comes from server config. - Helm refs are optional and mirrored to ops-runner, with no values in the files.
Found and fixed (059b675): a refresh refused with HTTP 200 and {"error":"bad_refresh_token"} was treated as retryable, so a revoked row stayed connected. It now ends in connection_error; the new test fails with the fix removed.
Open, not code in this repo (decision for Lily): the public Commonly Connect App asks for contents, issues and pull_requests at write. Only reads are pinned, but a person's stored refresh token can write to every repo they install the App on, and GitHub's install screen will ask for write while our copy says read. Unless writes are planned soon, cut the App to read before the walk. The hidden entry keeps this contained until then.
Pre-existing and left: tool calls on a grant already minted do not re-check hiding, so removing someone from the allowlist does not revoke grants they already hold.
Written by Vera, a Commonly agent — https://commonly.me/v2/pods/6a8f6dc7a1dccf2e02f31015
GATE: PASS
|
No actionable comments were generated in the recent review.
|
| Layer / File(s) | Summary |
|---|---|
GitHub tool entry and schemas backend/integrations/hostedMcp/githubMcp.ts, backend/integrations/hostedMcp/githubMcpSchemas.json, backend/integrations/hostedMcp/entries.ts, backend/__tests__/fixtures/hostedMcp/*, backend/__tests__/unit/services/hostedMcpEntry.test.js, frontend/src/v2/icons/platforms/index.tsx |
Registers six read-only GitHub tools and their pinned schemas. Tests compare the tool set with the captured source and check the GitHub entry configuration. |
GitHub OAuth and token responses backend/services/hostedMcpEntryService.ts, backend/services/hostedMcpIntakeService.ts, backend/services/hostedMcpCredentialService.ts, backend/__tests__/unit/services/hostedMcpIntake.test.js, backend/__tests__/unit/services/hostedMcpCredential.test.js, backend/__tests__/unit/routes/hostedMcpConnect.callback.test.js |
Adds an optional pinned token endpoint authentication method. A refresh response with an error and no access token is treated as a refusal, and bad_refresh_token is non-retryable. Tests cover these responses and explicitly empty granted scopes. |
Vendor access-page links backend/services/hostedMcpEntryService.ts, backend/integrations/hostedMcp/entries.ts, backend/integrations/hostedMcp/githubMcp.ts, backend/services/installable/installableCatalogService.ts, frontend/src/v2/components/V2ConnectorTools.tsx, frontend/src/i18n/locales/*.json, backend/__tests__/unit/services/installableCatalogService.test.js, frontend/src/v2/__tests__/V2ConnectorTools.test.tsx |
Adds optional HTTPS access pages to hosted MCP entries and catalog rows. The connector interface displays configured links on offers and on qualifying live grants. |
GitHub credentials and deployment configuration k8s/helm/commonly/values.yaml, k8s/helm/commonly/values-dev.yaml, k8s/helm/commonly/templates/secrets/api-keys.yaml, k8s/helm/commonly/templates/core/backend-deployment.yaml, k8s/helm/commonly/templates/ops/ops-runner-deployment.yaml, backend/__tests__/unit/integrations/hostedMcpClientWiring.test.js, backend/__tests__/unit/services/toolInstallables.test.js |
Adds GitHub MCP Helm settings and optional client credential references. Default values disable the integration; Dev values enable it and set the app slug. Tests cover credential readiness and chart wiring. |
Estimated code review effort: 3 (Moderate) | ~25 minutes
Change: Feature
Possibly related issues
- Team-Commonly/commonly#806: The PR treats
bad_refresh_tokenas a definitive hosted connection error, addressing the reported upstream authentication failure behavior. - Team-Commonly/commonly#791: The PR adds a GitHub hosted connector using per-person hosted connections, which relates to the issue’s GitHub identity boundary.
- Team-Commonly/commonly#1400: The PR adds access-page links to the V2 connector interface, addressing part of the reported connector discoverability area.
Possibly related PRs
- Team-Commonly/commonly#2215: Adds the original GitHub hosted MCP entry, fixture, and six pinned tools that this PR carries forward.
- Team-Commonly/commonly#2789: Implements the per-person GitHub connection flow that this PR supersedes and continues.
- Team-Commonly/commonly#2534: Changes hosted MCP callback and refresh paths also covered by this PR’s GitHub-specific tests.
Merge Risk: 
Minimal · up to 059b6
-
This adds a hidden, read-only GitHub connector for non-admins, with deployment wiring and a fix for a refresh refusal that GitHub returns as HTTP 200. The review found no concrete defects. The live GitHub token exchange and the dev secrets still need to be confirmed during the planned post-deployment walk, and the entry stays hidden until then.
Pre-merge checks: 
4
Finishing Touches
-
Generate docstrings
-
- Commit to this branch
- Create a new PR
Generate unit tests (beta)
-
- Commit to this branch
- Create a new PR
-
- Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts
Supersedes #2789 (Rivet, TASK-290). Same change rebased onto current main, plus one fix from the review below. Rivet's branch could not be updated from a seat. Row: TASK-334. Original description and history: #2789.
Tier 2 (auth: per-person connect). One review round, Opus-class, filed on this PR.
What it is
A hidden, per-person
github-mcphosted-MCP entry. A non-admin authorizes the public Commonly Connect GitHub App and installs it only on the repos they pick. The server-heldgithub-appcredential and its route stay admin-only. The entry ships in devhiddenEntriesuntil a fresh non-admin account walks it.Rebase
One conflict, in
values-dev.yaml. Main (#2950) moved the hidden-entry allowlist to a runner-only overlay, so I kept only thegithub-mcphidden line and dropped the inline allowlist block. The comment now points at the overlay.Added here
{"error":"bad_refresh_token"}, which is how GitHub answers. It was read as retryable, so a person who revoked at github.com/settings/applications stayedconnected. It now ends inconnection_errorlikeinvalid_grant. The new test fails with the fix removed.CodeRabbit on #2789
One thread (4374485130, read the commit write in the empty-scope test). Already fixed on the branch (
f5215e421); present here unchanged.Not in this PR
createAppfactory.contents,issuesandpull_requestsat write, but only six read tools are pinned. The App's permissions drop to read before the non-admin walk (Lily's ruling; the Cofounder makes the account change). That is a GitHub App setting, not repo code, and it does not block this PR because the entry ships hidden. Write returns with a write tool and its own approval flow.Verification
Node 22: the six backend suites for the entry pass (170 of 170), plus the wiring test (7 of 7) and the credential suite (50 of 50).
helm templaterenders againstvalues-dev.yaml.V2ConnectorToolspasses. Machine-local failures in unrelated frontend suites (openUiReact18,uiCardSource,V2UICard,V2MessageRow) come from a borrowed node_modules; I did not touch those files.🤖 Generated with Claude Code