Skip to content

feat(github): repository-scoped user tokens for per-repo imports#446

Open
guitavano wants to merge 1 commit into
mainfrom
feat/github-scope-token
Open

feat(github): repository-scoped user tokens for per-repo imports#446
guitavano wants to merge 1 commit into
mainfrom
feat/github-scope-token

Conversation

@guitavano
Copy link
Copy Markdown
Contributor

@guitavano guitavano commented May 26, 2026

Summary

  • Add GITHUB_SCOPE_TOKEN tool to mint repository-scoped user tokens via GitHub /applications/{client_id}/token/scoped
  • Pass repository_id during OAuth code exchange and refresh when provided in mesh client state / metadata
  • Authenticate scoped-token requests with GitHub App Basic auth and include required target (owner login)
  • Alias /api/mcp/mcp for local wrangler dev

Test plan

  • bun test github/server/lib/github-client.test.ts
  • Run bun run dev in github/ with .dev.vars configured
  • Call GITHUB_SCOPE_TOKEN with { repository_id, target } through mesh proxy
  • Deploy to github-mcp.decocms.com before merging dependent Studio PR

Made with Cursor


Summary by cubic

Adds repository-scoped GitHub user tokens to support per-repo imports. Introduces the GITHUB_SCOPE_TOKEN tool and threads repository_id through OAuth exchange/refresh; also aliases /api/mcp to /mcp for local dev.

  • New Features

    • Added GITHUB_SCOPE_TOKEN to mint repo-scoped tokens via GitHub /applications/{client_id}/token/scoped using App Basic auth; requires target (owner login).
    • Passes repository_id during OAuth code exchange and refresh when provided; parsed from Mesh client state (mesh:<base64url>).
    • Aliased /api/mcp to /mcp for wrangler dev.
  • Migration

    • Set GITHUB_CLIENT_ID and GITHUB_CLIENT_SECRET (see github/server/.dev.vars.example for local).
    • Send Mesh OAuth state with repositoryId to receive scoped tokens, and call GITHUB_SCOPE_TOKEN with { repository_id, target } after repo selection.

Written for commit 1cbdf7e. Summary will update on new commits. Review in cubic

Add GITHUB_SCOPE_TOKEN, repository_id on OAuth exchange/refresh, and Basic auth for GitHub scoped token API. Alias /api/mcp to /mcp for local wrangler.

Co-authored-by: Cursor <cursoragent@cursor.com>
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

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

3 issues found across 7 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="github/server/tools/scope-token.ts">

<violation number="1" location="github/server/tools/scope-token.ts:14">
P3: This adds duplicated OAuth credential-loading logic; extract to a shared helper to avoid drift between auth flows.</violation>

<violation number="2" location="github/server/tools/scope-token.ts:38">
P2: Trim `target` before `.min(1)` so whitespace-only owner logins are rejected at validation time.</violation>
</file>

<file name="github/server/lib/github-client.ts">

<violation number="1" location="github/server/lib/github-client.ts:97">
P2: `repository_id` is sent to `/login/oauth/access_token`, but this parameter is not documented for code exchange/refresh and may be ignored, so per-repo scoping may not actually apply during token mint/refresh.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

.describe("GitHub repository ID to scope the token to"),
target: z
.string()
.min(1)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2: Trim target before .min(1) so whitespace-only owner logins are rejected at validation time.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At github/server/tools/scope-token.ts, line 38:

<comment>Trim `target` before `.min(1)` so whitespace-only owner logins are rejected at validation time.</comment>

<file context>
@@ -0,0 +1,68 @@
+      .describe("GitHub repository ID to scope the token to"),
+    target: z
+      .string()
+      .min(1)
+      .describe(
+        "GitHub user or organization login that owns the repository (owner login)",
</file context>

}

if (options?.repositoryId !== undefined) {
body.repository_id = String(options.repositoryId);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2: repository_id is sent to /login/oauth/access_token, but this parameter is not documented for code exchange/refresh and may be ignored, so per-repo scoping may not actually apply during token mint/refresh.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At github/server/lib/github-client.ts, line 97:

<comment>`repository_id` is sent to `/login/oauth/access_token`, but this parameter is not documented for code exchange/refresh and may be ignored, so per-repo scoping may not actually apply during token mint/refresh.</comment>

<file context>
@@ -63,20 +71,30 @@ async function postToGitHub(
+  }
+
+  if (options?.repositoryId !== undefined) {
+    body.repository_id = String(options.repositoryId);
   }
 
</file context>

import { scopeUserAccessTokenToRepository } from "../lib/github-client.ts";
import type { Env } from "../types/env.ts";

function getOAuthCredentials(): { clientId: string; clientSecret: string } {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P3: This adds duplicated OAuth credential-loading logic; extract to a shared helper to avoid drift between auth flows.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At github/server/tools/scope-token.ts, line 14:

<comment>This adds duplicated OAuth credential-loading logic; extract to a shared helper to avoid drift between auth flows.</comment>

<file context>
@@ -0,0 +1,68 @@
+import { scopeUserAccessTokenToRepository } from "../lib/github-client.ts";
+import type { Env } from "../types/env.ts";
+
+function getOAuthCredentials(): { clientId: string; clientSecret: string } {
+  const clientId = process.env.GITHUB_CLIENT_ID || "";
+  const clientSecret = process.env.GITHUB_CLIENT_SECRET || "";
</file context>

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