Skip to content

Per-person GitHub connect for non-admins (supersedes #2789, TASK-290/334) - #3100

Merged
lilyshen0722 merged 11 commits into
mainfrom
vera/task-334-user-github
Oct 31, 2026
Merged

lilyshen0722 merged 11 commits into
mainfrom
vera/task-334-user-github

Conversation

@lilyshen0722

@lilyshen0722 lilyshen0722 commented Oct 31, 2026 •

Copy link
Copy Markdown
Contributor

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-mcp hosted-MCP entry. A non-admin authorizes the public Commonly Connect GitHub App and installs it only on the repos they pick. The server-held github-app credential and its route stay admin-only. The entry ships in dev hiddenEntries until 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 the github-mcp hidden line and dropped the inline allowlist block. The comment now points at the overlay.

Added here

  • A refused refresh can arrive as HTTP 200 with {"error":"bad_refresh_token"}, which is how GitHub answers. It was read as retryable, so a person who revoked at github.com/settings/applications stayed connected. It now ends in connection_error like invalid_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

  • The fresh non-admin HTTP scenario is TASK-299 and needs test(api): exercise wave-one scenarios through the production app #2687's createApp factory.
  • The live stranger walk runs after deploy, behind the hidden-entry allowlist.
  • App permissions: the public App asks for contents, issues and pull_requests at 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 template renders against values-dev.yaml. V2ConnectorTools passes. 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

lilyshen0722 and others added 11 commits October 31, 2026 14:29
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 lilyshen0722 left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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-app credential is not reachable. The entry's client comes from GITHUB_MCP_CLIENT_*, the broker spends only the person's own credential, and routes/integrations.ts is 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 are readOnlyHint: 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

@coderabbitai

coderabbitai Bot commented Oct 31, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

No actionable comments were generated in the recent review.

Recent review info
Run configuration
Commits

Reviewing files that changed from the base of the PR and between fd3a722 and 059b675.

Files selected for processing: 25
  • backend/__tests__/fixtures/hostedMcp/github-mcp-tools-source-2026-10-15.json
  • backend/__tests__/unit/integrations/hostedMcpClientWiring.test.js
  • backend/__tests__/unit/routes/hostedMcpConnect.callback.test.js
  • backend/__tests__/unit/services/hostedMcpCredential.test.js
  • backend/__tests__/unit/services/hostedMcpEntry.test.js
  • backend/__tests__/unit/services/hostedMcpIntake.test.js
  • backend/__tests__/unit/services/installableCatalogService.test.js
  • backend/__tests__/unit/services/toolInstallables.test.js
  • backend/integrations/hostedMcp/entries.ts
  • backend/integrations/hostedMcp/githubMcp.ts
  • backend/integrations/hostedMcp/githubMcpSchemas.json
  • backend/services/hostedMcpCredentialService.ts
  • backend/services/hostedMcpEntryService.ts
  • backend/services/hostedMcpIntakeService.ts
  • backend/services/installable/installableCatalogService.ts
  • frontend/src/i18n/locales/en.json
  • frontend/src/i18n/locales/zh-CN.json
  • frontend/src/v2/__tests__/V2ConnectorTools.test.tsx
  • frontend/src/v2/components/V2ConnectorTools.tsx
  • frontend/src/v2/icons/platforms/index.tsx
  • k8s/helm/commonly/templates/core/backend-deployment.yaml
  • k8s/helm/commonly/templates/ops/ops-runner-deployment.yaml
  • k8s/helm/commonly/templates/secrets/api-keys.yaml
  • k8s/helm/commonly/values-dev.yaml
  • k8s/helm/commonly/values.yaml

Limit details: You’ve used all 10 included reviews currently available.

Walkthrough

This pull request adds a read-only GitHub hosted MCP connector with six tools, GitHub App OAuth configuration, deployment settings, and vendor access links. It also changes handling for certain OAuth token responses.

Changes: GitHub Hosted MCP

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_token as 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

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: Passed 4
Check name Status Explanation
Title check Passed The title clearly identifies the main change: per-person GitHub connection for non-admin users. The superseded PR and task references add context but do not make the title misleading.
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Description check Passed The pull request description directly explains the GitHub MCP entry, OAuth and refresh-token changes, Kubernetes configuration, tests, and explicitly excluded work. It matches the changeset.
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

@lilyshen0722
lilyshen0722 added this pull request to the merge queue Oct 31, 2026
Merged via the queue into main with commit c2b4fb9 Oct 31, 2026
43 checks passed
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