Skip to content

Commit 922ad86

Browse files
authored
[MATRIX-1118] support transport type flag for MCP_SERVER (#3181)
1 parent f88414e commit 922ad86

13 files changed

+128
-16
lines changed

cmd/lint/main.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,7 @@ var vocabWords = []string{
255255
"googleai",
256256
"gzip",
257257
"hostname",
258+
"http",
258259
"https",
259260
"html",
260261
"iam",
@@ -321,11 +322,14 @@ var vocabWords = []string{
321322
"sso",
322323
"subresource",
323324
"stdin",
325+
"streamable",
326+
"streamable_http",
324327
"systest",
325328
"tableflow",
326329
"tcp",
327330
"transactional",
328331
"transitgateway",
332+
"transport_type",
329333
"txt",
330334
"ui",
331335
"undelete",

internal/flink/command_connection.go

Lines changed: 100 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package flink
33
import (
44
"fmt"
55
"slices"
6+
"strings"
67
"time"
78

89
"github.com/samber/lo"
@@ -96,6 +97,7 @@ func AddConnectionSecretFlags(cmd *cobra.Command) {
9697
cmd.Flags().String("client-secret", "", fmt.Sprintf("Specify OAuth2 client secret for the type: %s.", utils.ArrayToCommaDelimitedString(flink.ConnectionSecretTypeMapping["client-secret"], "or")))
9798
cmd.Flags().String("scope", "", fmt.Sprintf("Specify OAuth2 scope for the type: %s.", utils.ArrayToCommaDelimitedString(flink.ConnectionSecretTypeMapping["scope"], "or")))
9899
cmd.Flags().String("sse-endpoint", "", fmt.Sprintf("Specify SSE endpoint for the type: %s.", utils.ArrayToCommaDelimitedString(flink.ConnectionSecretTypeMapping["sse-endpoint"], "or")))
100+
cmd.Flags().String("transport-type", "", fmt.Sprintf("Specify transport type for the type: %s. Default: SSE.", utils.ArrayToCommaDelimitedString(flink.ConnectionSecretTypeMapping["transport-type"], "or")))
99101
cmd.MarkFlagsRequiredTogether("username", "password")
100102
cmd.MarkFlagsRequiredTogether("aws-access-key", "aws-secret-key")
101103
cmd.MarkFlagsRequiredTogether("token-endpoint", "client-id", "client-secret", "scope")
@@ -109,29 +111,51 @@ func validateConnectionType(connectionType string) error {
109111
return nil
110112
}
111113

112-
func validateConnectionSecrets(cmd *cobra.Command, connectionType string) (map[string]string, error) {
113-
var connectionSecrets []string
114-
connectionSecrets = append(connectionSecrets, flink.ConnectionTypeSecretMapping[connectionType]...)
114+
func validateSecretCompatibility(cmd *cobra.Command, connectionType string) error {
115+
connectionSecrets := flink.ConnectionTypeSecretMapping[connectionType]
115116

116117
for key := range flink.ConnectionSecretTypeMapping {
117118
secret, err := cmd.Flags().GetString(key)
118119
if err != nil {
119-
return nil, err
120+
return err
120121
}
121122
if secret != "" && !slices.Contains(connectionSecrets, key) {
122-
return nil, errors.NewErrorWithSuggestions(fmt.Sprintf("%s is invalid for connection %s.", key, connectionType), fmt.Sprintf("Valid secret types are %s.", utils.ArrayToCommaDelimitedString(connectionSecrets, "or")))
123+
return errors.NewErrorWithSuggestions(
124+
fmt.Sprintf("%s is invalid for connection %s.", key, connectionType),
125+
fmt.Sprintf("Valid secret types are %s.", utils.ArrayToCommaDelimitedString(connectionSecrets, "or")))
123126
}
124127
}
128+
return nil
129+
}
125130

126-
requiredSecretKeys := flink.ConnectionRequiredSecretMapping[connectionType]
127-
var optionalSecretKeys []string
128-
for _, secretKey := range flink.ConnectionTypeSecretMapping[connectionType] {
129-
if !slices.Contains(requiredSecretKeys, secretKey) {
130-
optionalSecretKeys = append(optionalSecretKeys, secretKey)
131+
func validateSecretValues(cmd *cobra.Command) error {
132+
for key, allowedValues := range flink.ConnectionSecretAllowedValues {
133+
secretValue, err := cmd.Flags().GetString(key)
134+
if err != nil {
135+
return err
136+
}
137+
if secretValue != "" && !containsCaseInsensitive(allowedValues, secretValue) {
138+
return errors.NewErrorWithSuggestions(
139+
fmt.Sprintf("%s is invalid value for flag %s.", secretValue, key),
140+
fmt.Sprintf("Valid values for flag %s are %s.", key, utils.ArrayToCommaDelimitedString(allowedValues, "or")))
141+
}
142+
}
143+
return nil
144+
}
145+
146+
func containsCaseInsensitive(slice []string, item string) bool {
147+
for _, s := range slice {
148+
if strings.EqualFold(s, item) {
149+
return true
131150
}
132151
}
152+
return false
153+
}
133154

155+
func buildRequiredSecretsMap(cmd *cobra.Command, connectionType string) (map[string]string, error) {
156+
requiredSecretKeys := flink.ConnectionRequiredSecretMapping[connectionType]
134157
secretMap := map[string]string{}
158+
135159
for _, requiredKey := range requiredSecretKeys {
136160
secret, err := cmd.Flags().GetString(requiredKey)
137161
if err != nil {
@@ -146,23 +170,47 @@ func validateConnectionSecrets(cmd *cobra.Command, connectionType string) (map[s
146170
}
147171
secretMap[backendKey] = secret
148172
}
173+
return secretMap, nil
174+
}
175+
176+
func addOptionalSecretsToMap(cmd *cobra.Command, connectionType string, secretMap map[string]string) error {
177+
requiredSecretKeys := flink.ConnectionRequiredSecretMapping[connectionType]
178+
var optionalSecretKeys []string
179+
180+
for _, secretKey := range flink.ConnectionTypeSecretMapping[connectionType] {
181+
if !slices.Contains(requiredSecretKeys, secretKey) {
182+
optionalSecretKeys = append(optionalSecretKeys, secretKey)
183+
}
184+
}
149185

150186
for _, optionalSecretKey := range optionalSecretKeys {
151187
secret, err := cmd.Flags().GetString(optionalSecretKey)
152188
if err != nil {
153-
return nil, err
189+
return err
154190
}
155191

156192
backendKey, ok := flink.ConnectionSecretBackendKeyMapping[optionalSecretKey]
157193
if !ok {
158-
return nil, fmt.Errorf("backend key not found for %s", optionalSecretKey)
194+
return fmt.Errorf("backend key not found for %s", optionalSecretKey)
159195
}
160196

161197
if secret != "" {
162198
secretMap[backendKey] = secret
163199
}
164200
}
201+
return nil
202+
}
203+
204+
func validateTransportTypeRules(secretMap map[string]string) error {
205+
if transportType, ok := secretMap["TRANSPORT_TYPE"]; ok && transportType == "STREAMABLE_HTTP" {
206+
if _, ok := secretMap["SSE_ENDPOINT"]; ok {
207+
return fmt.Errorf("sse-endpoint flag is not allowed for STREAMABLE_HTTP transport-type")
208+
}
209+
}
210+
return nil
211+
}
165212

213+
func determineAndSetAuthType(secretMap map[string]string) {
166214
if _, ok := secretMap["API_KEY"]; ok {
167215
secretMap[authType] = "API_KEY"
168216
} else if _, ok := secretMap["USERNAME"]; ok {
@@ -172,13 +220,52 @@ func validateConnectionSecrets(cmd *cobra.Command, connectionType string) (map[s
172220
} else if _, ok := secretMap["OAUTH2_CLIENT_ID"]; ok {
173221
secretMap[authType] = "OAUTH2"
174222
}
223+
}
175224

225+
func validateRequiredAuthSecrets(connectionType string, secretMap map[string]string) error {
176226
if secretMap[authType] == "" && slices.Contains(types.GetKeys(flink.ConnectionOneOfRequiredSecretsMapping), connectionType) {
177-
return nil, fmt.Errorf("no secrets provided for type %s, one of the required secrets %s must be provided", connectionType,
227+
return fmt.Errorf("no secrets provided for type %s, one of the required secrets %s must be provided", connectionType,
178228
utils.ArrayToCommaDelimitedString(lo.Map(flink.ConnectionOneOfRequiredSecretsMapping[connectionType], func(item []string, _ int) string {
179229
return fmt.Sprintf("%s", item)
180230
}), "or"))
181231
}
232+
return nil
233+
}
234+
235+
func validateConnectionSecrets(cmd *cobra.Command, connectionType string) (map[string]string, error) {
236+
// Validate secret compatibility with connection type
237+
if err := validateSecretCompatibility(cmd, connectionType); err != nil {
238+
return nil, err
239+
}
240+
241+
// Validate secret values against allowed values
242+
if err := validateSecretValues(cmd); err != nil {
243+
return nil, err
244+
}
245+
246+
// Build secret map from required secrets
247+
secretMap, err := buildRequiredSecretsMap(cmd, connectionType)
248+
if err != nil {
249+
return nil, err
250+
}
251+
252+
// Add optional secrets to the map
253+
if err := addOptionalSecretsToMap(cmd, connectionType, secretMap); err != nil {
254+
return nil, err
255+
}
256+
257+
// Validate transport type specific rules
258+
if err := validateTransportTypeRules(secretMap); err != nil {
259+
return nil, err
260+
}
261+
262+
// Determine and set authentication type
263+
determineAndSetAuthType(secretMap)
264+
265+
// Validate required authentication secrets
266+
if err := validateRequiredAuthSecrets(connectionType, secretMap); err != nil {
267+
return nil, err
268+
}
182269

183270
return secretMap, nil
184271
}

pkg/flink/utils.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ var (
1616
"couchbase": {"username", "password"},
1717
"confluent_jdbc": {"username", "password"},
1818
"rest": {"username", "password", "token", "token-endpoint", "client-id", "client-secret", "scope"},
19-
"mcp_server": {"api-key", "token", "token-endpoint", "client-id", "client-secret", "scope", "sse-endpoint"},
19+
"mcp_server": {"api-key", "token", "token-endpoint", "client-id", "client-secret", "scope", "sse-endpoint", "transport-type"},
2020
}
2121

2222
ConnectionSecretTypeMapping = map[string][]string{
@@ -33,6 +33,7 @@ var (
3333
"client-secret": {"rest", "mcp_server"},
3434
"scope": {"rest", "mcp_server"},
3535
"sse-endpoint": {"mcp_server"},
36+
"transport-type": {"mcp_server"},
3637
}
3738

3839
ConnectionRequiredSecretMapping = map[string][]string{
@@ -57,6 +58,10 @@ var (
5758
"mcp_server": {{"api-key"}, {"token"}, {"token-endpoint", "client-id", "client-secret", "scope"}},
5859
}
5960

61+
ConnectionSecretAllowedValues = map[string][]string{
62+
"transport-type": {"SSE", "STREAMABLE_HTTP"},
63+
}
64+
6065
ConnectionSecretBackendKeyMapping = map[string]string{
6166
"api-key": "API_KEY",
6267
"aws-access-key": "AWS_ACCESS_KEY_ID",
@@ -71,5 +76,6 @@ var (
7176
"client-secret": "OAUTH2_CLIENT_SECRET",
7277
"scope": "OAUTH2_SCOPE",
7378
"sse-endpoint": "SSE_ENDPOINT",
79+
"transport-type": "TRANSPORT_TYPE",
7480
}
7581
)

test/fixtures/output/flink/connection/create-help.golden

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ Flags:
2626
--client-secret string Specify OAuth2 client secret for the type: "rest" or "mcp_server".
2727
--scope string Specify OAuth2 scope for the type: "rest" or "mcp_server".
2828
--sse-endpoint string Specify SSE endpoint for the type: "mcp_server".
29+
--transport-type string Specify transport type for the type: "mcp_server". Default: SSE.
2930
--environment string Environment ID.
3031
-o, --output string Specify the output format as "human", "json", or "yaml". (default "human")
3132

test/fixtures/output/flink/connection/create/create-mcp_server-missing-required-secret.golden

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ Flags:
2525
--client-secret string Specify OAuth2 client secret for the type: "rest" or "mcp_server".
2626
--scope string Specify OAuth2 scope for the type: "rest" or "mcp_server".
2727
--sse-endpoint string Specify SSE endpoint for the type: "mcp_server".
28+
--transport-type string Specify transport type for the type: "mcp_server". Default: SSE.
2829
--environment string Environment ID.
2930
-o, --output string Specify the output format as "human", "json", or "yaml". (default "human")
3031

test/fixtures/output/flink/connection/create/create-mcp_server-mutually-exclusive-secret.golden

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ Flags:
2525
--client-secret string Specify OAuth2 client secret for the type: "rest" or "mcp_server".
2626
--scope string Specify OAuth2 scope for the type: "rest" or "mcp_server".
2727
--sse-endpoint string Specify SSE endpoint for the type: "mcp_server".
28+
--transport-type string Specify transport type for the type: "mcp_server". Default: SSE.
2829
--environment string Environment ID.
2930
-o, --output string Specify the output format as "human", "json", or "yaml". (default "human")
3031

test/fixtures/output/flink/connection/create/create-missing-required-secret.golden

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ Flags:
2525
--client-secret string Specify OAuth2 client secret for the type: "rest" or "mcp_server".
2626
--scope string Specify OAuth2 scope for the type: "rest" or "mcp_server".
2727
--sse-endpoint string Specify SSE endpoint for the type: "mcp_server".
28+
--transport-type string Specify transport type for the type: "mcp_server". Default: SSE.
2829
--environment string Environment ID.
2930
-o, --output string Specify the output format as "human", "json", or "yaml". (default "human")
3031

test/fixtures/output/flink/connection/create/create-rest-missing-required-secret.golden

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ Flags:
2525
--client-secret string Specify OAuth2 client secret for the type: "rest" or "mcp_server".
2626
--scope string Specify OAuth2 scope for the type: "rest" or "mcp_server".
2727
--sse-endpoint string Specify SSE endpoint for the type: "mcp_server".
28+
--transport-type string Specify transport type for the type: "mcp_server". Default: SSE.
2829
--environment string Environment ID.
2930
-o, --output string Specify the output format as "human", "json", or "yaml". (default "human")
3031

test/fixtures/output/flink/connection/create/create-rest-mutually-exclusive-secret.golden

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ Flags:
2525
--client-secret string Specify OAuth2 client secret for the type: "rest" or "mcp_server".
2626
--scope string Specify OAuth2 scope for the type: "rest" or "mcp_server".
2727
--sse-endpoint string Specify SSE endpoint for the type: "mcp_server".
28+
--transport-type string Specify transport type for the type: "mcp_server". Default: SSE.
2829
--environment string Environment ID.
2930
-o, --output string Specify the output format as "human", "json", or "yaml". (default "human")
3031

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Error: sse-endpoint flag is not allowed for STREAMABLE_HTTP transport-type

0 commit comments

Comments
 (0)