Skip to content

Commit 997264b

Browse files
committed
Serve oauth protected resource metadata endpoint
1 parent e2bab0b commit 997264b

File tree

6 files changed

+53
-6
lines changed

6 files changed

+53
-6
lines changed

pkg/config/config.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,11 @@ type StaticConfig struct {
1919
// When true, expose only tools annotated with readOnlyHint=true
2020
ReadOnly bool `toml:"read_only,omitempty"`
2121
// When true, disable tools annotated with destructiveHint=true
22-
DisableDestructive bool `toml:"disable_destructive,omitempty"`
23-
EnabledTools []string `toml:"enabled_tools,omitempty"`
24-
DisabledTools []string `toml:"disabled_tools,omitempty"`
25-
RequireOAuth bool `toml:"require_oauth,omitempty"`
22+
DisableDestructive bool `toml:"disable_destructive,omitempty"`
23+
EnabledTools []string `toml:"enabled_tools,omitempty"`
24+
DisabledTools []string `toml:"disabled_tools,omitempty"`
25+
RequireOAuth bool `toml:"require_oauth,omitempty"`
26+
AuthorizationServer string `toml:"authorization_server,omitempty"`
2627
}
2728

2829
type GroupVersionKind struct {

pkg/http/authorization.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ const (
2323
func AuthorizationMiddleware(requireOAuth bool, mcpServer *mcp.Server) func(http.Handler) http.Handler {
2424
return func(next http.Handler) http.Handler {
2525
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
26-
if r.URL.Path == "/healthz" {
26+
if r.URL.Path == "/healthz" || r.URL.Path == "/.well-known/oauth-protected-resource" {
2727
next.ServeHTTP(w, r)
2828
return
2929
}

pkg/http/http.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package http
22

33
import (
44
"context"
5+
"encoding/json"
56
"errors"
67
"net/http"
78
"os"
@@ -35,6 +36,28 @@ func Serve(ctx context.Context, mcpServer *mcp.Server, staticConfig *config.Stat
3536
mux.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) {
3637
w.WriteHeader(http.StatusOK)
3738
})
39+
mux.HandleFunc("/.well-known/oauth-protected-resource", func(w http.ResponseWriter, r *http.Request) {
40+
w.Header().Set("Content-Type", "application/json")
41+
42+
var authServers []string
43+
if staticConfig.AuthorizationServer != "" {
44+
authServers = []string{staticConfig.AuthorizationServer}
45+
} else {
46+
// Fallback to Kubernetes API server host if authorization_server is not configured
47+
if apiServerHost := mcpServer.GetKubernetesAPIServerHost(); apiServerHost != "" {
48+
authServers = []string{apiServerHost}
49+
}
50+
}
51+
52+
response := map[string]interface{}{
53+
"authorization_servers": authServers,
54+
}
55+
56+
w.WriteHeader(http.StatusOK)
57+
if err := json.NewEncoder(w).Encode(response); err != nil {
58+
http.Error(w, err.Error(), http.StatusInternalServerError)
59+
}
60+
})
3861

3962
ctx, cancel := context.WithCancel(ctx)
4063
defer cancel()

pkg/kubernetes-mcp-server/cmd/root.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ type MCPServerOptions struct {
5656
ReadOnly bool
5757
DisableDestructive bool
5858
RequireOAuth bool
59+
AuthorizationURL string
5960

6061
ConfigPath string
6162
StaticConfig *config.StaticConfig
@@ -110,7 +111,8 @@ func NewMCPServer(streams genericiooptions.IOStreams) *cobra.Command {
110111
cmd.Flags().BoolVar(&o.DisableDestructive, "disable-destructive", o.DisableDestructive, "If true, tools annotated with destructiveHint=true are disabled")
111112
cmd.Flags().BoolVar(&o.RequireOAuth, "require-oauth", o.RequireOAuth, "If true, requires OAuth authorization as defined in the Model Context Protocol (MCP) specification. This flag is ignored if transport type is stdio")
112113
cmd.Flags().MarkHidden("require-oauth")
113-
114+
cmd.Flags().StringVar(&o.AuthorizationURL, "authorization-url", o.AuthorizationURL, "OAuth authorization server URL for protected resource endpoint. If not provided, the Kubernetes API server host will be used. Only valid if require-oauth is enabled.")
115+
cmd.Flags().MarkHidden("authorization-url")
114116
return cmd
115117
}
116118

@@ -164,6 +166,9 @@ func (m *MCPServerOptions) loadFlags(cmd *cobra.Command) {
164166
if cmd.Flag("require-oauth").Changed {
165167
m.StaticConfig.RequireOAuth = m.RequireOAuth
166168
}
169+
if cmd.Flag("authorization-url").Changed {
170+
m.StaticConfig.AuthorizationServer = m.AuthorizationURL
171+
}
167172
}
168173

169174
func (m *MCPServerOptions) initializeLogging() {
@@ -182,6 +187,9 @@ func (m *MCPServerOptions) Validate() error {
182187
if m.Port != "" && (m.SSEPort > 0 || m.HttpPort > 0) {
183188
return fmt.Errorf("--port is mutually exclusive with deprecated --http-port and --sse-port flags")
184189
}
190+
if !m.StaticConfig.RequireOAuth && m.StaticConfig.AuthorizationServer != "" {
191+
return fmt.Errorf("authorization-url is only valid if require-oauth is enabled")
192+
}
185193
return nil
186194
}
187195

pkg/kubernetes/kubernetes.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,13 @@ func (m *Manager) Close() {
125125
}
126126
}
127127

128+
func (m *Manager) GetAPIServerHost() string {
129+
if m.cfg == nil {
130+
return ""
131+
}
132+
return m.cfg.Host
133+
}
134+
128135
func (m *Manager) ToDiscoveryClient() (discovery.CachedDiscoveryInterface, error) {
129136
return m.discoveryClient, nil
130137
}

pkg/mcp/mcp.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,14 @@ func (s *Server) VerifyToken(ctx context.Context, token string, audience string)
114114
return s.k.VerifyToken(ctx, token, audience)
115115
}
116116

117+
// GetKubernetesAPIServerHost returns the Kubernetes API server host from the configuration.
118+
func (s *Server) GetKubernetesAPIServerHost() string {
119+
if s.k == nil {
120+
return ""
121+
}
122+
return s.k.GetAPIServerHost()
123+
}
124+
117125
func (s *Server) Close() {
118126
if s.k != nil {
119127
s.k.Close()

0 commit comments

Comments
 (0)