@@ -11,45 +11,116 @@ import (
1111 "time"
1212)
1313
14- // IsRemoteServer detects if a service is a remote MCP server by checking if the command starts with https://
14+ // IsRemoteServer detects if a service is a remote MCP server by checking if the command starts with https:// or http://
1515func IsRemoteServer (service Service ) bool {
16- return strings .HasPrefix (service .Command , "https://" )
16+ return strings .HasPrefix (service .Command , "https://" ) || strings . HasPrefix ( service . Command , "http://" )
1717}
1818
19- // ValidateRemoteServerOAuth validates that a remote server has all required OAuth labels
20- func ValidateRemoteServerOAuth ( name string , service Service ) error {
21- requiredLabels := [] string {
22- "mcp.grant-type" ,
23- "mcp.token-endpoint" ,
24- "mcp.client-id" ,
25- "mcp.client-secret" ,
19+ // UsesHeadersAuth checks if a remote server uses headers-based authentication instead of OAuth
20+ func UsesHeadersAuth ( service Service ) bool {
21+ // Check if any mcp.header.* labels exist
22+ for label := range service . Labels {
23+ if strings . HasPrefix ( label , "mcp.header." ) {
24+ return true
25+ }
2626 }
27+ return false
28+ }
2729
28- var missingLabels []string
29- for _ , label := range requiredLabels {
30- if _ , exists := service .Labels [label ]; ! exists {
31- missingLabels = append (missingLabels , label )
32- }
30+ // ValidateRemoteServerAuth validates that a remote server has either OAuth or headers-based auth configured
31+ func ValidateRemoteServerAuth (name string , service Service ) error {
32+ usesHeaders := UsesHeadersAuth (service )
33+ hasOAuthLabels := service .Labels ["mcp.grant-type" ] != ""
34+
35+ if ! usesHeaders && ! hasOAuthLabels {
36+ return fmt .Errorf ("remote server '%s' must have either OAuth labels (mcp.grant-type, mcp.token-endpoint, mcp.client-id, mcp.client-secret) or headers labels (mcp.header.*)" , name )
3337 }
3438
35- if len (missingLabels ) > 0 {
36- return fmt .Errorf ("remote server '%s' missing required OAuth labels: %s" ,
37- name , strings .Join (missingLabels , ", " ))
39+ if usesHeaders && hasOAuthLabels {
40+ return fmt .Errorf ("remote server '%s' cannot have both OAuth labels and headers labels" , name )
3841 }
3942
40- // Validate grant type is client_credentials
41- if grantType := service .Labels ["mcp.grant-type" ]; grantType != "client_credentials" {
42- return fmt .Errorf ("remote server '%s' must use 'client_credentials' grant type, got: %s" ,
43- name , grantType )
43+ if hasOAuthLabels {
44+ // Validate OAuth configuration
45+ requiredLabels := []string {
46+ "mcp.grant-type" ,
47+ "mcp.token-endpoint" ,
48+ "mcp.client-id" ,
49+ "mcp.client-secret" ,
50+ }
51+
52+ var missingLabels []string
53+ for _ , label := range requiredLabels {
54+ if _ , exists := service .Labels [label ]; ! exists {
55+ missingLabels = append (missingLabels , label )
56+ }
57+ }
58+
59+ if len (missingLabels ) > 0 {
60+ return fmt .Errorf ("remote server '%s' missing required OAuth labels: %s" ,
61+ name , strings .Join (missingLabels , ", " ))
62+ }
63+
64+ // Validate grant type is client_credentials
65+ if grantType := service .Labels ["mcp.grant-type" ]; grantType != "client_credentials" {
66+ return fmt .Errorf ("remote server '%s' must use 'client_credentials' grant type, got: %s" ,
67+ name , grantType )
68+ }
4469 }
4570
4671 return nil
4772}
4873
74+ // ValidateRemoteServerOAuth validates that a remote server has all required OAuth labels (deprecated, use ValidateRemoteServerAuth)
75+ func ValidateRemoteServerOAuth (name string , service Service ) error {
76+ return ValidateRemoteServerAuth (name , service )
77+ }
78+
79+ // ExtractHeaders extracts headers from service labels (mcp.header.*) with environment variable expansion
80+ func ExtractHeaders (service Service , envVars map [string ]string ) (map [string ]string , error ) {
81+ headers := make (map [string ]string )
82+ hasHeaders := false
83+
84+ for label , value := range service .Labels {
85+ if strings .HasPrefix (label , "mcp.header." ) {
86+ hasHeaders = true
87+ // Extract header name (everything after "mcp.header.")
88+ headerName := strings .TrimPrefix (label , "mcp.header." )
89+ if headerName == "" {
90+ continue
91+ }
92+
93+ // Skip empty placeholder headers (e.g., "X-Empty: "" for servers that need no auth)
94+ if headerName == "X-Empty" && value == "" {
95+ continue
96+ }
97+
98+ // Expand environment variables in header value
99+ expandedValue := expandEnvVars (value , envVars )
100+
101+ // Validate that environment variables were resolved
102+ if strings .Contains (expandedValue , "${" ) || (strings .Contains (expandedValue , "$" ) && ! strings .HasPrefix (expandedValue , "$" )) {
103+ return nil , fmt .Errorf ("environment variable in header '%s' was not resolved: %s" , headerName , expandedValue )
104+ }
105+
106+ headers [headerName ] = expandedValue
107+ }
108+ }
109+
110+ // Return headers map (can be empty for servers with no authentication)
111+ // If no mcp.header.* labels exist at all, that's an error (use OAuth or headers)
112+ if ! hasHeaders {
113+ return nil , fmt .Errorf ("no headers found (expected mcp.header.* labels)" )
114+ }
115+
116+ return headers , nil
117+ }
118+
49119// remoteSupportedTools defines which tools support remote MCP servers
50120var remoteSupportedTools = map [string ]bool {
51- "kiro" : true ,
52- "q-cli" : true ,
121+ "cursor" : true ,
122+ "kiro" : true ,
123+ "q-cli" : true ,
53124}
54125
55126// ValidateToolSupport validates that the specified tool supports remote servers if any are present
0 commit comments