Skip to content

feat(oauth): wire stdio OAuth 2.1 login into the server (2/4)#2710

Merged
SamMorrowDrums merged 5 commits into
sammorrowdrums/oauth-stdio-corefrom
sammorrowdrums/oauth-stdio-wiring
Jun 26, 2026
Merged

feat(oauth): wire stdio OAuth 2.1 login into the server (2/4)#2710
SamMorrowDrums merged 5 commits into
sammorrowdrums/oauth-stdio-corefrom
sammorrowdrums/oauth-stdio-wiring

Conversation

@SamMorrowDrums

@SamMorrowDrums SamMorrowDrums commented Jun 16, 2026

Copy link
Copy Markdown
Collaborator

Part 2 of 4 — stdio wiring

Stacked on #2704 (PR 1/4, internal/oauth core library). Review and merge #2704 first; this PR's diff is only meaningful on top of it.

What this does

Connects the internal/oauth core library to the stdio MCP server so users can authenticate with an OAuth App or GitHub App client ID instead of a static personal access token. The token is acquired lazily on the first tool call and auto-refreshes for the rest of the session.

This is stdio-only and deliberately does not touch MCP-HTTP auth.

Changes

  • BearerAuthTransport.TokenProvider — consulted per request, taking precedence over the static Token. Lets the lazily-acquired, refreshing OAuth token take effect without rebuilding the client.
  • createGitHubClients — when a TokenProvider is set, authenticates via BearerAuthTransport and skips go-github's WithAuthToken (which would install its own round tripper pinning a static token).
  • RunStdioServer — starts without a token when an OAuthManager is configured, and installs receiving middleware that runs the authorization flow on the first tools/call, before any handler tries to call GitHub with an empty token.
  • sessionPrompter — adapts the MCP server session to oauth.Prompter, surfacing the auth URL / device code via elicitation (URL → form), keeping the authorization URL out of the model's context. Falls back to a tool-result message when elicitation is unavailable.
  • Scope-based tool filtering — uses the requested OAuth scopes. The default supported set hides nothing; a narrower --oauth-scopes both narrows the grant and filters tools accordingly.
  • New stdio flags--oauth-client-id, --oauth-client-secret, --oauth-scopes, --oauth-callback-port (env: GITHUB_OAUTH_*).

Works for both OAuth Apps and GitHub Apps (same endpoints/params; the refreshing TokenSource absorbs GitHub App user-token expiry).

Testing

  • pkg/http/transport/bearer_test.go — static token, provider precedence, per-request resolution (refresh), GraphQL-Features passthrough, no original-request mutation.
  • internal/ghmcp/oauth_test.gosessionPrompter exercised against a real in-memory MCP session (capability matrix + accept/decline/cancel × url/form); middleware tested at each branch via a deterministic fake authenticator; createGitHubClients token-provider wiring proven against an httptest server (current token used per request, no stale pin).

The middleware depends on a small oauthAuthenticator interface (2 real impls: *oauth.Manager + test fake) so it can be exercised without standing up live GitHub flows — a genuine seam, not faux-DRY.

Stack

Replaces #1836.


Update — pre-merge review fix (commit 2b4d5e6)

Clarified the SupportedScopes doc comment to record its dual role: stdio OAuth login requests these scopes by default and filters the exposed tools to the scopes actually granted, so a tool whose required scope is missing from the list is hidden under default OAuth even though a PAT carrying that scope would expose it. Keeps the list honest as scopes change. (Picked up the rebased core-lib fixes from 1/4.)

Copilot AI left a comment

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.

Pull request overview

This PR (2/4 in the OAuth stack) wires the new internal/oauth library into the stdio MCP server so authentication can be performed lazily via OAuth (with refresh) instead of requiring a static PAT up-front.

Changes:

  • Add per-request token resolution via BearerAuthTransport.TokenProvider and propagate it through stdio GitHub client creation.
  • Add stdio OAuth wiring (OAuth manager + first-tool-call receiving middleware + scope-based tool filtering).
  • Add stdio CLI flags/env support for OAuth client ID/secret/scopes/callback port and add focused tests for the new behavior.
Show a summary per file
File Description
pkg/http/transport/bearer.go Adds TokenProvider support for per-request bearer token resolution.
pkg/http/transport/bearer_test.go Adds unit tests for static token vs provider precedence, per-request refresh, and header passthrough.
pkg/github/server.go Extends MCPServerConfig with TokenProvider for dynamic auth.
internal/ghmcp/server.go Wires token provider into REST/GraphQL clients; adds stdio OAuth manager config + middleware + OAuth scope filtering.
internal/ghmcp/oauth.go Implements MCP-session→OAuth prompter adapter and the receiving middleware that triggers auth on first tools/call.
internal/ghmcp/oauth_test.go Adds tests for session prompter capability handling and OAuth middleware/client wiring.
cmd/github-mcp-server/main.go Adds stdio CLI flags and constructs OAuth manager/scopes when no PAT is provided.

Copilot's findings

  • Files reviewed: 7/7 changed files
  • Comments generated: 3

Comment thread pkg/http/transport/bearer.go Outdated
Comment on lines +24 to +28
token := t.Token
if t.TokenProvider != nil {
token = t.TokenProvider()
}
req.Header.Set(headers.AuthorizationHeader, "Bearer "+token)

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Good catch on the cleanliness point. Fixed in 9ab8046: the transport now omits the Authorization header entirely when the token is empty (pre-authorization), so we send a properly unauthenticated request rather than an empty Bearer value. Tests updated to expect no header. (Note: the prior tests did pass — Header.Get trims the trailing space — but omitting the header is clearer and more correct.)

Comment thread internal/ghmcp/oauth.go
Comment on lines +39 to +45
func (p *sessionPrompter) PromptURL(ctx context.Context, prompt oauth.Prompt) error {
res, err := p.session.Elicit(ctx, &mcp.ElicitParams{
Mode: "url",
Message: prompt.Message,
URL: prompt.URL,
ElicitationID: rand.Text(),
})

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

crypto/rand.Text() is a standard-library function added in Go 1.24 (this module targets Go 1.25 — see go.mod), so it compiles and is exercised by the green CI build across macOS/Linux/Windows. It returns a cryptographically random RFC 4648 base32 string, which is exactly what we want for a unique ElicitationID. Leaving as-is.

Comment thread internal/ghmcp/server.go
@@ -255,22 +283,34 @@ func RunStdioServer(cfg StdioServerConfig) error {
logger := slog.New(slogHandler)
logger.Info("starting server", "version", cfg.Version, "host", cfg.Host, "readOnly", cfg.ReadOnly, "lockdownEnabled", cfg.LockdownMode)

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Agreed — enforced in 9ab8046. RunStdioServer now returns an error up front if both a static Token and an OAuthManager are set, instead of silently using OAuth for auth and the static token for scope filtering. Added TestRunStdioServerRejectsTokenAndOAuth to cover it. (The CLI already only sets one or the other, but RunStdioServer is exported and used as a library, so the guard is worth having.)

Copilot AI left a comment

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.

Copilot's findings

  • Files reviewed: 7/7 changed files
  • Comments generated: 6

Comment on lines +52 to +72
var gotAuth string
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
gotAuth = r.Header.Get(headers.AuthorizationHeader)
w.WriteHeader(http.StatusOK)
}))
defer server.Close()

rt := &BearerAuthTransport{
Transport: http.DefaultTransport,
Token: tc.token,
TokenProvider: tc.tokenProvider,
}

req, err := http.NewRequestWithContext(context.Background(), http.MethodGet, server.URL, nil)
require.NoError(t, err)

resp, err := rt.RoundTrip(req)
require.NoError(t, err)
defer resp.Body.Close()

assert.Equal(t, tc.wantAuth, gotAuth)
Comment on lines +84 to +103
var gotAuth string
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
gotAuth = r.Header.Get(headers.AuthorizationHeader)
w.WriteHeader(http.StatusOK)
}))
defer server.Close()

current := ""
rt := &BearerAuthTransport{
Transport: http.DefaultTransport,
TokenProvider: func() string { return current },
}

do := func() {
req, err := http.NewRequestWithContext(context.Background(), http.MethodGet, server.URL, nil)
require.NoError(t, err)
resp, err := rt.RoundTrip(req)
require.NoError(t, err)
defer resp.Body.Close()
}
Comment on lines +120 to +140
var gotFeatures string
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
gotFeatures = r.Header.Get(headers.GraphQLFeaturesHeader)
w.WriteHeader(http.StatusOK)
}))
defer server.Close()

rt := &BearerAuthTransport{
Transport: http.DefaultTransport,
Token: "token",
}

ctx := ghcontext.WithGraphQLFeatures(context.Background(), "feature1", "feature2")
req, err := http.NewRequestWithContext(ctx, http.MethodPost, server.URL, nil)
require.NoError(t, err)

resp, err := rt.RoundTrip(req)
require.NoError(t, err)
defer resp.Body.Close()

assert.Equal(t, "feature1, feature2", gotFeatures)
Comment thread internal/ghmcp/oauth.go
Comment on lines +102 to +123
return func(ctx context.Context, method string, request mcp.Request) (mcp.Result, error) {
if method != "tools/call" || mgr.HasToken() {
return next(ctx, method, request)
}

callReq, ok := request.(*mcp.CallToolRequest)
if !ok {
return next(ctx, method, request)
}

outcome, err := mgr.Authenticate(ctx, &sessionPrompter{session: callReq.Session})
if err != nil {
return nil, fmt.Errorf("github authorization failed: %w", err)
}
if outcome != nil && outcome.UserAction != nil {
logger.Info("surfacing github authorization instructions to user")
return &mcp.CallToolResult{
Content: []mcp.Content{&mcp.TextContent{Text: outcome.UserAction.Message}},
}, nil
}
return next(ctx, method, request)
}
// to log in via the browser-based OAuth flow on first use. Works for both
// OAuth Apps and GitHub Apps.
stdioCmd.Flags().String("oauth-client-id", "", "OAuth App or GitHub App client ID, enabling interactive OAuth login when no token is set")
stdioCmd.Flags().String("oauth-client-secret", "", "OAuth client secret, if the app requires one (it is a public, non-confidential credential for distributed clients)")
Comment on lines +312 to +333
var gotAuth string
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
gotAuth = r.Header.Get(headers.AuthorizationHeader)
w.WriteHeader(http.StatusOK)
}))
defer server.Close()

current := ""
apiHost, err := utils.NewAPIHost(server.URL)
require.NoError(t, err)

clients, err := createGitHubClients(github.MCPServerConfig{
Version: "test",
TokenProvider: func() string { return current },
}, apiHost)
require.NoError(t, err)

do := func() {
resp, err := clients.rest.Client().Get(server.URL)
require.NoError(t, err)
defer resp.Body.Close()
}
SamMorrowDrums and others added 3 commits June 18, 2026 10:57
Connect the internal/oauth core library to the stdio MCP server so users
can authenticate with an OAuth App or GitHub App client ID instead of a
static personal access token.

- BearerAuthTransport gains a TokenProvider that is consulted per request,
  letting the lazily-acquired, auto-refreshing OAuth token take effect
  without rebuilding the client.
- createGitHubClients uses BearerAuthTransport (and skips go-github's
  WithAuthToken, which would pin a static token) when a TokenProvider is set.
- RunStdioServer starts without a token and installs receiving middleware
  that runs the authorization flow on the first tool call, surfacing the
  auth URL or device code via elicitation (or a tool result as a fallback).
- Tool filtering uses the requested OAuth scopes; the default supported set
  hides nothing, while a narrower --oauth-scopes both narrows the grant and
  filters tools accordingly.
- A sessionPrompter adapts the MCP server session to oauth.Prompter, keeping
  the authorization URL off the model's context.
- New stdio flags: --oauth-client-id/-client-secret/-scopes/-callback-port.

This is stdio-only and deliberately does not touch MCP-HTTP auth.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…en/oauth

- BearerAuthTransport omits the Authorization header entirely when the token
  is empty (pre-authorization) rather than sending an empty "Bearer " value.
- RunStdioServer rejects the ambiguous combination of a static Token and an
  OAuthManager up front, enforcing the documented mutual exclusivity.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…lter

Document that stdio OAuth login requests these scopes by default and then
filters the exposed tools to the scopes actually granted, so a tool whose
required scope is absent from this list is hidden under default OAuth even
though a PAT carrying that scope would expose it. Keep the list in sync with
tool scope requirements when scopes change.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@SamMorrowDrums SamMorrowDrums force-pushed the sammorrowdrums/oauth-stdio-wiring branch from c3b677d to 2b4d5e6 Compare June 18, 2026 09:00
@SamMorrowDrums SamMorrowDrums marked this pull request as ready for review June 19, 2026 08:27
@SamMorrowDrums SamMorrowDrums requested a review from a team as a code owner June 19, 2026 08:27
An elicitation prompt that the client cannot deliver (a transport or
protocol failure) was treated the same as a user actively declining: any
display error cancelled the flow. That conflated a system failure with a
deliberate "no", so a client that advertised URL elicitation but failed
to deliver it would hard-fail the login instead of degrading.

Add an ErrPromptUnavailable sentinel alongside ErrPromptDeclined and have
the MCP adapter return it when Elicit fails at the transport level. The
manager now falls back to the manual user-action channel on an
undeliverable prompt (keeping the background flow alive so the user can
still authorize out of band), while a genuine decline still aborts. A
context-cancelled prompt is checked first so an ending flow is never
misread as a transport failure.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
… (3/4) (#2711)

* build(oauth): bake in default OAuth credentials via build-time ldflags

Inject the public OAuth client credentials (stored as the OAUTH_CLIENT_ID
and OAUTH_CLIENT_SECRET repo secrets) at build time via -ldflags so
official binaries and images ship a working default app for zero-config
login. Security relies on PKCE, not on the secret. Local/dev builds leave
the values empty and continue to require an explicit token or
--oauth-client-id.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix(oauth): recognize github.com host aliases for the baked-in client

Match the default host via oauth.NormalizeHost instead of only an empty
host string, so an explicit GITHUB_HOST=github.com (or api.github.com)
still counts as the default and keeps zero-config baked-in login working.
GHES and ghe.com users continue to bring their own --oauth-client-id.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* docs(oauth): document stdio OAuth login; make PAT optional in install config (#2717)

Add a dedicated Local Server OAuth Login guide (docs/oauth-login.md) covering
the PKCE/device flows, display channels and the URL-elicitation security
advisory, scope-based tool filtering, the fixed-port Docker recipe and its
loopback/port-safety behavior, bringing your own OAuth or GitHub App, and the
GitHub Enterprise Server / ghe.com requirement to register an app on that host
(custom --gh-host directs login at that instance's authorization server).

Reflect that the local server now logs in with OAuth by default on github.com:
- README: make the stdio Docker install badges OAuth-first (fixed callback port
  8085 published to loopback), drop the PAT prompt, and reframe the PAT as an
  optional alternative with a pointer to the new guide.
- server.json: make GITHUB_PERSONAL_ACCESS_TOKEN optional and publish the OAuth
  callback port so the registry default works without a token.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@SamMorrowDrums SamMorrowDrums merged commit 5da25d8 into sammorrowdrums/oauth-stdio-core Jun 26, 2026
12 of 15 checks passed
@SamMorrowDrums SamMorrowDrums deleted the sammorrowdrums/oauth-stdio-wiring branch June 26, 2026 09:58
SamMorrowDrums added a commit that referenced this pull request Jun 26, 2026
* feat(oauth): add stdio OAuth 2.1 stdio login
Introduce internal/oauth, a self-contained library that performs the
user-facing GitHub OAuth login the stdio server uses to obtain a token
without a pre-provisioned PAT. It is independent of MCP: client concerns
(elicitation) sit behind the Prompter interface so the flows are testable
without a live session.

What it provides:
- Authorization-code + PKCE flow with a local loopback callback server,
  state/CSRF validation, and XSS-safe result pages.
- Device-authorization flow as a fallback (headless, containers).
- A Manager that selects the most secure available channel
  (browser auto-open -> URL elicitation -> last-resort user action),
  runs a single flow at a time, and exposes a refreshing token source.

Both GitHub OAuth Apps and GitHub Apps are supported without special
casing: the token is modeled as an x/oauth2 refreshing TokenSource, so
expiring GitHub App user tokens are renewed transparently (the gap that
made a stored-token approach silently die after ~8h).

When a client lacks secure URL elicitation and the flow falls back to a
tool-response message, the message advises the user that their agent/CLI/
IDE does not appear to support URL elicitation and suggests requesting it
for improved security.

Tests exercise real protocol behavior against an httptest GitHub stand-in:
PKCE challenge/verifier, GitHub App refresh-on-expiry, device polling,
URL elicitation, declined prompts, the last-resort action with advisory,
and single-flight concurrency.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix(oauth): reap browser launcher and keep native callback on loopback

Address code review:
- openBrowser: reap the launcher process asynchronously so it does not
  linger as a zombie for the lifetime of the server.
- listenCallback: take an explicit bindAll flag and bind to all interfaces
  only inside a container (where the published port arrives via eth0).
  A native run, even with a fixed callback port, now stays on 127.0.0.1
  instead of 0.0.0.0.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix(oauth): fail fast when a fixed callback port is unavailable

A fixed --oauth-callback-port is registered with the OAuth app and chosen
deliberately, so a bind failure means another process holds the port and
could intercept the authorization redirect. Treat that as fatal instead of
silently downgrading to the device flow, which would mask the conflict.

Also warn, when binding the callback inside a container, that the listener
is on all interfaces and should be published to loopback only so the
authorization code is not exposed on the container network.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix(oauth): surface refresh failures, bound refresh, prefer device flow when headless

Addresses pre-merge review of the OAuth stdio core:

- Log a one-time warning when token refresh fails instead of silently
  returning an empty access token, so a forced re-login isn't a surprise.
- Bound each background token refresh with a 30s HTTP client timeout so a
  stalled GitHub token endpoint can't block tool calls indefinitely.
- On a headless host (no display server) with a random callback port, fall
  back to the device-code flow — the only channel reachable from a browser
  on another machine — instead of dead-ending on an unreachable localhost
  redirect. A generic browser-open failure still offers the manual URL.
- Mark the callback bind failure with a sentinel so the fixed-port-busy
  fatal path can't misreport an unrelated error as a port conflict.
- Export NormalizeHost so callers can recognize the default github.com host
  (consumed by the build-time baked-in credential guard).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* feat(oauth): wire stdio OAuth 2.1 login into the server (2/4) (#2710)

* feat(oauth): wire stdio OAuth 2.1 login into the server

Connect the internal/oauth core library to the stdio MCP server so users
can authenticate with an OAuth App or GitHub App client ID instead of a
static personal access token.

- BearerAuthTransport gains a TokenProvider that is consulted per request,
  letting the lazily-acquired, auto-refreshing OAuth token take effect
  without rebuilding the client.
- createGitHubClients uses BearerAuthTransport (and skips go-github's
  WithAuthToken, which would pin a static token) when a TokenProvider is set.
- RunStdioServer starts without a token and installs receiving middleware
  that runs the authorization flow on the first tool call, surfacing the
  auth URL or device code via elicitation (or a tool result as a fallback).
- Tool filtering uses the requested OAuth scopes; the default supported set
  hides nothing, while a narrower --oauth-scopes both narrows the grant and
  filters tools accordingly.
- A sessionPrompter adapts the MCP server session to oauth.Prompter, keeping
  the authorization URL off the model's context.
- New stdio flags: --oauth-client-id/-client-secret/-scopes/-callback-port.

This is stdio-only and deliberately does not touch MCP-HTTP auth.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* refactor(oauth): address review — omit empty bearer header, guard token/oauth

- BearerAuthTransport omits the Authorization header entirely when the token
  is empty (pre-authorization) rather than sending an empty "Bearer " value.
- RunStdioServer rejects the ambiguous combination of a static Token and an
  OAuthManager up front, enforcing the documented mutual exclusivity.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* docs(oauth): clarify SupportedScopes is the stdio default and tool filter

Document that stdio OAuth login requests these scopes by default and then
filters the exposed tools to the scopes actually granted, so a tool whose
required scope is absent from this list is hidden under default OAuth even
though a PAT carrying that scope would expose it. Keep the list in sync with
tool scope requirements when scopes change.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Distinguish undeliverable auth prompts from user declines

An elicitation prompt that the client cannot deliver (a transport or
protocol failure) was treated the same as a user actively declining: any
display error cancelled the flow. That conflated a system failure with a
deliberate "no", so a client that advertised URL elicitation but failed
to deliver it would hard-fail the login instead of degrading.

Add an ErrPromptUnavailable sentinel alongside ErrPromptDeclined and have
the MCP adapter return it when Elicit fails at the transport level. The
manager now falls back to the manual user-action channel on an
undeliverable prompt (keeping the background flow alive so the user can
still authorize out of band), while a genuine decline still aborts. A
context-cancelled prompt is checked first so an ending flow is never
misread as a transport failure.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* build(oauth): bake in default OAuth credentials for official releases (3/4) (#2711)

* build(oauth): bake in default OAuth credentials via build-time ldflags

Inject the public OAuth client credentials (stored as the OAUTH_CLIENT_ID
and OAUTH_CLIENT_SECRET repo secrets) at build time via -ldflags so
official binaries and images ship a working default app for zero-config
login. Security relies on PKCE, not on the secret. Local/dev builds leave
the values empty and continue to require an explicit token or
--oauth-client-id.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix(oauth): recognize github.com host aliases for the baked-in client

Match the default host via oauth.NormalizeHost instead of only an empty
host string, so an explicit GITHUB_HOST=github.com (or api.github.com)
still counts as the default and keeps zero-config baked-in login working.
GHES and ghe.com users continue to bring their own --oauth-client-id.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* docs(oauth): document stdio OAuth login; make PAT optional in install config (#2717)

Add a dedicated Local Server OAuth Login guide (docs/oauth-login.md) covering
the PKCE/device flows, display channels and the URL-elicitation security
advisory, scope-based tool filtering, the fixed-port Docker recipe and its
loopback/port-safety behavior, bringing your own OAuth or GitHub App, and the
GitHub Enterprise Server / ghe.com requirement to register an app on that host
(custom --gh-host directs login at that instance's authorization server).

Reflect that the local server now logs in with OAuth by default on github.com:
- README: make the stdio Docker install badges OAuth-first (fixed callback port
  8085 published to loopback), drop the PAT prompt, and reframe the PAT as an
  optional alternative with a pointer to the new guide.
- server.json: make GITHUB_PERSONAL_ACCESS_TOKEN optional and publish the OAuth
  callback port so the registry default works without a token.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
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.

3 participants