mirror of
https://github.com/alibaba/higress.git
synced 2026-06-06 03:07:28 +08:00
fix: add match list and wasm mcp-server message pub in redis (#1963)
This commit is contained in:
@@ -53,6 +53,16 @@ type SSEServer struct {
|
||||
Config map[string]interface{} `json:"config,omitempty"`
|
||||
}
|
||||
|
||||
// MatchRule defines a rule for matching requests
|
||||
type MatchRule struct {
|
||||
// Domain pattern, supports wildcards
|
||||
MatchRuleDomain string `json:"match_rule_domain,omitempty"`
|
||||
// Path pattern to match
|
||||
MatchRulePath string `json:"match_rule_path,omitempty"`
|
||||
// Type of match rule: exact, prefix, suffix, contains, regex
|
||||
MatchRuleType string `json:"match_rule_type,omitempty"`
|
||||
}
|
||||
|
||||
// McpServer defines the configuration for MCP (Model Context Protocol) server
|
||||
type McpServer struct {
|
||||
// Flag to control whether MCP server is enabled
|
||||
@@ -63,10 +73,16 @@ type McpServer struct {
|
||||
SsePathSuffix string `json:"sse_path_suffix,omitempty"`
|
||||
// List of SSE servers Configs
|
||||
Servers []*SSEServer `json:"servers,omitempty"`
|
||||
// List of match rules for filtering requests
|
||||
MatchList []*MatchRule `json:"match_list,omitempty"`
|
||||
}
|
||||
|
||||
func NewDefaultMcpServer() *McpServer {
|
||||
return &McpServer{Enable: false}
|
||||
return &McpServer{
|
||||
Enable: false,
|
||||
Servers: make([]*SSEServer, 0),
|
||||
MatchList: make([]*MatchRule, 0),
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
@@ -82,6 +98,26 @@ func validMcpServer(m *McpServer) error {
|
||||
return errors.New("redis config cannot be empty when mcp server is enabled")
|
||||
}
|
||||
|
||||
// Validate match rule types
|
||||
if m.MatchList != nil {
|
||||
validTypes := map[string]bool{
|
||||
"exact": true,
|
||||
"prefix": true,
|
||||
"suffix": true,
|
||||
"contains": true,
|
||||
"regex": true,
|
||||
}
|
||||
|
||||
for _, rule := range m.MatchList {
|
||||
if rule.MatchRuleType == "" {
|
||||
return errors.New("match_rule_type cannot be empty, must be one of: exact, prefix, suffix, contains, regex")
|
||||
}
|
||||
if !validTypes[rule.MatchRuleType] {
|
||||
return fmt.Errorf("invalid match_rule_type: %s, must be one of: exact, prefix, suffix, contains, regex", rule.MatchRuleType)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -134,6 +170,17 @@ func deepCopyMcpServer(mcp *McpServer) (*McpServer, error) {
|
||||
}
|
||||
}
|
||||
|
||||
if len(mcp.MatchList) > 0 {
|
||||
newMcp.MatchList = make([]*MatchRule, len(mcp.MatchList))
|
||||
for i, rule := range mcp.MatchList {
|
||||
newMcp.MatchList[i] = &MatchRule{
|
||||
MatchRuleDomain: rule.MatchRuleDomain,
|
||||
MatchRulePath: rule.MatchRulePath,
|
||||
MatchRuleType: rule.MatchRuleType,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return newMcp, nil
|
||||
}
|
||||
|
||||
@@ -268,7 +315,7 @@ func (m *McpServerController) ConstructEnvoyFilters() ([]*config.Config, error)
|
||||
}
|
||||
|
||||
func (m *McpServerController) constructMcpServerStruct(mcp *McpServer) string {
|
||||
// 构建 servers 配置
|
||||
// Build servers configuration
|
||||
servers := "[]"
|
||||
if len(mcp.Servers) > 0 {
|
||||
serverConfigs := make([]string, len(mcp.Servers))
|
||||
@@ -291,7 +338,21 @@ func (m *McpServerController) constructMcpServerStruct(mcp *McpServer) string {
|
||||
servers = fmt.Sprintf("[%s]", strings.Join(serverConfigs, ","))
|
||||
}
|
||||
|
||||
// 构建完整的配置结构
|
||||
// Build match_list configuration
|
||||
matchList := "[]"
|
||||
if len(mcp.MatchList) > 0 {
|
||||
matchConfigs := make([]string, len(mcp.MatchList))
|
||||
for i, rule := range mcp.MatchList {
|
||||
matchConfigs[i] = fmt.Sprintf(`{
|
||||
"match_rule_domain": "%s",
|
||||
"match_rule_path": "%s",
|
||||
"match_rule_type": "%s"
|
||||
}`, rule.MatchRuleDomain, rule.MatchRulePath, rule.MatchRuleType)
|
||||
}
|
||||
matchList = fmt.Sprintf("[%s]", strings.Join(matchConfigs, ","))
|
||||
}
|
||||
|
||||
// Build complete configuration structure
|
||||
structFmt := `{
|
||||
"name": "envoy.filters.http.golang",
|
||||
"typed_config": {
|
||||
@@ -310,6 +371,7 @@ func (m *McpServerController) constructMcpServerStruct(mcp *McpServer) string {
|
||||
"db": %d
|
||||
},
|
||||
"sse_path_suffix": "%s",
|
||||
"match_list": %s,
|
||||
"servers": %s
|
||||
}
|
||||
}
|
||||
@@ -323,5 +385,6 @@ func (m *McpServerController) constructMcpServerStruct(mcp *McpServer) string {
|
||||
mcp.Redis.Password,
|
||||
mcp.Redis.DB,
|
||||
mcp.SsePathSuffix,
|
||||
matchList,
|
||||
servers)
|
||||
}
|
||||
|
||||
@@ -31,7 +31,9 @@ func Test_validMcpServer(t *testing.T) {
|
||||
{
|
||||
name: "default",
|
||||
mcp: &McpServer{
|
||||
Enable: false,
|
||||
Enable: false,
|
||||
MatchList: []*MatchRule{},
|
||||
Servers: []*SSEServer{},
|
||||
},
|
||||
wantErr: nil,
|
||||
},
|
||||
@@ -43,8 +45,10 @@ func Test_validMcpServer(t *testing.T) {
|
||||
{
|
||||
name: "enabled but no redis config",
|
||||
mcp: &McpServer{
|
||||
Enable: true,
|
||||
Redis: nil,
|
||||
Enable: true,
|
||||
Redis: nil,
|
||||
MatchList: []*MatchRule{},
|
||||
Servers: []*SSEServer{},
|
||||
},
|
||||
wantErr: errors.New("redis config cannot be empty when mcp server is enabled"),
|
||||
},
|
||||
@@ -59,6 +63,13 @@ func Test_validMcpServer(t *testing.T) {
|
||||
DB: 0,
|
||||
},
|
||||
SsePathSuffix: "/sse",
|
||||
MatchList: []*MatchRule{
|
||||
{
|
||||
MatchRuleDomain: "*",
|
||||
MatchRulePath: "*",
|
||||
MatchRuleType: "exact",
|
||||
},
|
||||
},
|
||||
Servers: []*SSEServer{
|
||||
{
|
||||
Name: "test-server",
|
||||
@@ -104,6 +115,8 @@ func Test_compareMcpServer(t *testing.T) {
|
||||
Redis: &RedisConfig{
|
||||
Address: "localhost:6379",
|
||||
},
|
||||
MatchList: []*MatchRule{},
|
||||
Servers: []*SSEServer{},
|
||||
},
|
||||
new: nil,
|
||||
wantResult: ResultDelete,
|
||||
@@ -116,12 +129,16 @@ func Test_compareMcpServer(t *testing.T) {
|
||||
Redis: &RedisConfig{
|
||||
Address: "localhost:6379",
|
||||
},
|
||||
MatchList: []*MatchRule{},
|
||||
Servers: []*SSEServer{},
|
||||
},
|
||||
new: &McpServer{
|
||||
Enable: true,
|
||||
Redis: &RedisConfig{
|
||||
Address: "localhost:6379",
|
||||
},
|
||||
MatchList: []*MatchRule{},
|
||||
Servers: []*SSEServer{},
|
||||
},
|
||||
wantResult: ResultNothing,
|
||||
wantErr: nil,
|
||||
@@ -133,12 +150,22 @@ func Test_compareMcpServer(t *testing.T) {
|
||||
Redis: &RedisConfig{
|
||||
Address: "localhost:6379",
|
||||
},
|
||||
MatchList: []*MatchRule{},
|
||||
Servers: []*SSEServer{},
|
||||
},
|
||||
new: &McpServer{
|
||||
Enable: true,
|
||||
Redis: &RedisConfig{
|
||||
Address: "redis:6379",
|
||||
},
|
||||
MatchList: []*MatchRule{
|
||||
{
|
||||
MatchRuleDomain: "*",
|
||||
MatchRulePath: "/test",
|
||||
MatchRuleType: "exact",
|
||||
},
|
||||
},
|
||||
Servers: []*SSEServer{},
|
||||
},
|
||||
wantResult: ResultReplace,
|
||||
wantErr: nil,
|
||||
@@ -171,6 +198,8 @@ func Test_deepCopyMcpServer(t *testing.T) {
|
||||
Password: "password",
|
||||
DB: 0,
|
||||
},
|
||||
MatchList: []*MatchRule{},
|
||||
Servers: []*SSEServer{},
|
||||
},
|
||||
wantMcp: &McpServer{
|
||||
Enable: true,
|
||||
@@ -180,6 +209,8 @@ func Test_deepCopyMcpServer(t *testing.T) {
|
||||
Password: "password",
|
||||
DB: 0,
|
||||
},
|
||||
MatchList: []*MatchRule{},
|
||||
Servers: []*SSEServer{},
|
||||
},
|
||||
wantErr: nil,
|
||||
},
|
||||
@@ -194,6 +225,13 @@ func Test_deepCopyMcpServer(t *testing.T) {
|
||||
DB: 0,
|
||||
},
|
||||
SsePathSuffix: "/sse",
|
||||
MatchList: []*MatchRule{
|
||||
{
|
||||
MatchRuleDomain: "*",
|
||||
MatchRulePath: "*",
|
||||
MatchRuleType: "exact",
|
||||
},
|
||||
},
|
||||
Servers: []*SSEServer{
|
||||
{
|
||||
Name: "test-server",
|
||||
@@ -214,6 +252,13 @@ func Test_deepCopyMcpServer(t *testing.T) {
|
||||
DB: 0,
|
||||
},
|
||||
SsePathSuffix: "/sse",
|
||||
MatchList: []*MatchRule{
|
||||
{
|
||||
MatchRuleDomain: "*",
|
||||
MatchRulePath: "*",
|
||||
MatchRuleType: "exact",
|
||||
},
|
||||
},
|
||||
Servers: []*SSEServer{
|
||||
{
|
||||
Name: "test-server",
|
||||
@@ -280,6 +325,8 @@ func TestMcpServerController_AddOrUpdateHigressConfig(t *testing.T) {
|
||||
Password: "password",
|
||||
DB: 0,
|
||||
},
|
||||
Servers: []*SSEServer{},
|
||||
MatchList: []*MatchRule{},
|
||||
},
|
||||
},
|
||||
wantErr: nil,
|
||||
@@ -292,6 +339,8 @@ func TestMcpServerController_AddOrUpdateHigressConfig(t *testing.T) {
|
||||
Password: "password",
|
||||
DB: 0,
|
||||
},
|
||||
Servers: []*SSEServer{},
|
||||
MatchList: []*MatchRule{},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -302,6 +351,8 @@ func TestMcpServerController_AddOrUpdateHigressConfig(t *testing.T) {
|
||||
Redis: &RedisConfig{
|
||||
Address: "localhost:6379",
|
||||
},
|
||||
Servers: []*SSEServer{},
|
||||
MatchList: []*MatchRule{},
|
||||
},
|
||||
},
|
||||
new: &HigressConfig{
|
||||
@@ -310,6 +361,8 @@ func TestMcpServerController_AddOrUpdateHigressConfig(t *testing.T) {
|
||||
Redis: &RedisConfig{
|
||||
Address: "redis:6379",
|
||||
},
|
||||
Servers: []*SSEServer{},
|
||||
MatchList: []*MatchRule{},
|
||||
},
|
||||
},
|
||||
wantErr: nil,
|
||||
@@ -319,6 +372,8 @@ func TestMcpServerController_AddOrUpdateHigressConfig(t *testing.T) {
|
||||
Redis: &RedisConfig{
|
||||
Address: "redis:6379",
|
||||
},
|
||||
Servers: []*SSEServer{},
|
||||
MatchList: []*MatchRule{},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -329,6 +384,8 @@ func TestMcpServerController_AddOrUpdateHigressConfig(t *testing.T) {
|
||||
Redis: &RedisConfig{
|
||||
Address: "localhost:6379",
|
||||
},
|
||||
Servers: []*SSEServer{},
|
||||
MatchList: []*MatchRule{},
|
||||
},
|
||||
},
|
||||
new: &HigressConfig{
|
||||
|
||||
Reference in New Issue
Block a user