Skip to content

Commit 08ff307

Browse files
committed
Implement --token and MCP_GITHUB_TOKEN login support
Signed-off-by: Radoslav Dimitrov <[email protected]>
1 parent 6c0f085 commit 08ff307

File tree

3 files changed

+90
-38
lines changed

3 files changed

+90
-38
lines changed

cmd/publisher/auth/github-at.go

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,10 @@ type StoredRegistryToken struct {
5050

5151
// GitHubATProvider implements the Provider interface using GitHub's device flow
5252
type GitHubATProvider struct {
53-
clientID string
54-
forceLogin bool
55-
registryURL string
53+
clientID string
54+
forceLogin bool
55+
registryURL string
56+
providedToken string // Token provided via --token flag or MCP_GITHUB_TOKEN env var
5657
}
5758

5859
// ServerHealthResponse represents the response from the health endpoint
@@ -62,10 +63,16 @@ type ServerHealthResponse struct {
6263
}
6364

6465
// NewGitHubATProvider creates a new GitHub OAuth provider
65-
func NewGitHubATProvider(forceLogin bool, registryURL string) Provider {
66+
func NewGitHubATProvider(forceLogin bool, registryURL, token string) Provider {
67+
// Check for token from flag or environment variable
68+
if token == "" {
69+
token = os.Getenv("MCP_GITHUB_TOKEN")
70+
}
71+
6672
return &GitHubATProvider{
67-
forceLogin: forceLogin,
68-
registryURL: registryURL,
73+
forceLogin: forceLogin,
74+
registryURL: registryURL,
75+
providedToken: token,
6976
}
7077
}
7178

@@ -100,6 +107,11 @@ func (g *GitHubATProvider) GetToken(ctx context.Context) (string, error) {
100107

101108
// NeedsLogin checks if a new login is required
102109
func (g *GitHubATProvider) NeedsLogin() bool {
110+
// If a token was provided via --token or MCP_GITHUB_TOKEN, no login needed
111+
if g.providedToken != "" {
112+
return false
113+
}
114+
103115
if g.forceLogin {
104116
return true
105117
}
@@ -123,6 +135,15 @@ func (g *GitHubATProvider) NeedsLogin() bool {
123135

124136
// Login performs the GitHub device flow authentication
125137
func (g *GitHubATProvider) Login(ctx context.Context) error {
138+
// If a token was provided via --token or MCP_GITHUB_TOKEN, save it and skip device flow
139+
if g.providedToken != "" {
140+
err := saveToken(g.providedToken)
141+
if err != nil {
142+
return fmt.Errorf("error saving provided token: %w", err)
143+
}
144+
return nil
145+
}
146+
126147
// If clientID is not set, try to retrieve it from the server's health endpoint
127148
if g.clientID == "" {
128149
clientID, err := getClientID(ctx, g.registryURL)

cmd/publisher/commands/init.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ func InitCommand() error {
2626
description := detectDescription()
2727
version := "1.0.0"
2828
repoURL := detectRepoURL()
29-
repoSource := "github"
29+
repoSource := MethodGitHub
3030
if repoURL != "" && !strings.Contains(repoURL, "github.com") {
3131
if strings.Contains(repoURL, "gitlab.com") {
3232
repoSource = "gitlab"

cmd/publisher/commands/login.go

Lines changed: 62 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,11 @@ import (
1515
const (
1616
DefaultRegistryURL = "https://registry.modelcontextprotocol.io"
1717
TokenFileName = ".mcp_publisher_token" //nolint:gosec // Not a credential, just a filename
18+
MethodGitHub = "github"
19+
MethodGitHubOIDC = "github-oidc"
20+
MethodDNS = "dns"
21+
MethodHTTP = "http"
22+
MethodNone = "none"
1823
)
1924

2025
type CryptoAlgorithm auth.CryptoAlgorithm
@@ -32,56 +37,82 @@ func (c *CryptoAlgorithm) Set(v string) error {
3237
return fmt.Errorf("invalid algorithm: %q (allowed: ed25519, ecdsap384)", v)
3338
}
3439

40+
type loginFlags struct {
41+
domain string
42+
privateKey string
43+
cryptoAlgorithm CryptoAlgorithm
44+
registryURL string
45+
token string
46+
}
47+
3548
func LoginCommand(args []string) error {
3649
if len(args) < 1 {
3750
return errors.New("authentication method required\n\nUsage: mcp-publisher login <method>\n\nMethods:\n github Interactive GitHub authentication\n github-oidc GitHub Actions OIDC authentication\n dns DNS-based authentication (requires --domain and --private-key)\n http HTTP-based authentication (requires --domain and --private-key)\n none Anonymous authentication (for testing)")
3851
}
3952

4053
method := args[0]
54+
flags, err := parseLoginFlags(method, args[1:])
55+
if err != nil {
56+
return err
57+
}
58+
59+
authProvider, err := createAuthProvider(method, flags)
60+
if err != nil {
61+
return err
62+
}
4163

42-
// Parse remaining flags based on method
43-
loginFlags := flag.NewFlagSet("login", flag.ExitOnError)
44-
var domain string
45-
var privateKey string
46-
var cryptoAlgorithm = CryptoAlgorithm(auth.AlgorithmEd25519)
47-
var registryURL string
64+
return performLogin(authProvider, method, flags.registryURL)
65+
}
66+
67+
func parseLoginFlags(method string, args []string) (*loginFlags, error) {
68+
flags := &loginFlags{
69+
cryptoAlgorithm: CryptoAlgorithm(auth.AlgorithmEd25519), // default
70+
}
71+
loginFlagSet := flag.NewFlagSet("login", flag.ExitOnError)
4872

49-
loginFlags.StringVar(&registryURL, "registry", DefaultRegistryURL, "Registry URL")
73+
loginFlagSet.StringVar(&flags.registryURL, "registry", DefaultRegistryURL, "Registry URL")
5074

51-
if method == "dns" || method == "http" {
52-
loginFlags.StringVar(&domain, "domain", "", "Domain name")
53-
loginFlags.StringVar(&privateKey, "private-key", "", "Private key (hex)")
54-
loginFlags.Var(&cryptoAlgorithm, "algorithm", "Cryptographic algorithm (ed25519, ecdsap384)")
75+
if method == MethodGitHub {
76+
loginFlagSet.StringVar(&flags.token, "token", "", "GitHub Personal Access Token")
5577
}
5678

57-
if err := loginFlags.Parse(args[1:]); err != nil {
58-
return err
79+
if method == MethodDNS || method == MethodHTTP {
80+
loginFlagSet.StringVar(&flags.domain, "domain", "", "Domain name")
81+
loginFlagSet.StringVar(&flags.privateKey, "private-key", "", "Private key (64-char hex)")
82+
loginFlagSet.Var(&flags.cryptoAlgorithm, "algorithm", "Cryptographic algorithm (ed25519, ecdsap384)")
5983
}
6084

61-
// Create auth provider based on method
62-
var authProvider auth.Provider
85+
if err := loginFlagSet.Parse(args); err != nil {
86+
return nil, err
87+
}
88+
89+
return flags, nil
90+
}
91+
92+
func createAuthProvider(method string, flags *loginFlags) (auth.Provider, error) {
6393
switch method {
64-
case "github":
65-
authProvider = auth.NewGitHubATProvider(true, registryURL)
66-
case "github-oidc":
67-
authProvider = auth.NewGitHubOIDCProvider(registryURL)
68-
case "dns":
69-
if domain == "" || privateKey == "" {
70-
return errors.New("dns authentication requires --domain and --private-key")
94+
case MethodGitHub:
95+
return auth.NewGitHubATProvider(true, flags.registryURL, flags.token), nil
96+
case MethodGitHubOIDC:
97+
return auth.NewGitHubOIDCProvider(flags.registryURL), nil
98+
case MethodDNS:
99+
if flags.domain == "" || flags.privateKey == "" {
100+
return nil, errors.New("dns authentication requires --domain and --private-key")
71101
}
72-
authProvider = auth.NewDNSProvider(registryURL, domain, privateKey, auth.CryptoAlgorithm(cryptoAlgorithm))
73-
case "http":
74-
if domain == "" || privateKey == "" {
75-
return errors.New("http authentication requires --domain and --private-key")
102+
return auth.NewDNSProvider(flags.registryURL, flags.domain, flags.privateKey, auth.CryptoAlgorithm(flags.cryptoAlgorithm)), nil
103+
case MethodHTTP:
104+
if flags.domain == "" || flags.privateKey == "" {
105+
return nil, errors.New("http authentication requires --domain and --private-key")
76106
}
77-
authProvider = auth.NewHTTPProvider(registryURL, domain, privateKey, auth.CryptoAlgorithm(cryptoAlgorithm))
78-
case "none":
79-
authProvider = auth.NewNoneProvider(registryURL)
107+
return auth.NewHTTPProvider(flags.registryURL, flags.domain, flags.privateKey, auth.CryptoAlgorithm(flags.cryptoAlgorithm)), nil
108+
case MethodNone:
109+
return auth.NewNoneProvider(flags.registryURL), nil
80110
default:
81-
return fmt.Errorf("unknown authentication method: %s\nFor a list of available methods, run: mcp-publisher login", method)
111+
return nil, fmt.Errorf("unknown authentication method: %s\nFor a list of available methods, run: mcp-publisher login", method)
82112
}
113+
}
83114

84-
// Perform login
115+
func performLogin(authProvider auth.Provider, method, registryURL string) error {
85116
ctx := context.Background()
86117
_, _ = fmt.Fprintf(os.Stdout, "Logging in with %s...\n", method)
87118

0 commit comments

Comments
 (0)