mirror of
https://github.com/alibaba/higress.git
synced 2026-03-20 19:17:43 +08:00
feat: Refactor mcpServer.matchList config generation logic (#2207)
This commit is contained in:
@@ -27,6 +27,7 @@ import (
|
||||
apiv1 "github.com/alibaba/higress/api/networking/v1"
|
||||
"github.com/alibaba/higress/pkg/common"
|
||||
common2 "github.com/alibaba/higress/pkg/ingress/kube/common"
|
||||
"github.com/alibaba/higress/pkg/ingress/kube/mcpserver"
|
||||
provider "github.com/alibaba/higress/registry"
|
||||
"github.com/alibaba/higress/registry/memory"
|
||||
"github.com/golang/protobuf/ptypes/wrappers"
|
||||
@@ -56,7 +57,26 @@ const (
|
||||
DefaultRefreshIntervalLimit = time.Second * 10
|
||||
DefaultFetchPageSize = 50
|
||||
DefaultJoiner = "@@"
|
||||
NacosV3LabelKey = "isV3"
|
||||
)
|
||||
|
||||
var (
|
||||
supportedProtocols = map[string]bool{
|
||||
provider.HttpProtocol: true,
|
||||
provider.McpSSEProtocol: true,
|
||||
provider.McpStreambleProtocol: true,
|
||||
}
|
||||
protocolUpstreamTypeMapping = map[string]string{
|
||||
provider.HttpProtocol: mcpserver.UpstreamTypeRest,
|
||||
provider.McpSSEProtocol: mcpserver.UpstreamTypeSSE,
|
||||
provider.McpStreambleProtocol: mcpserver.UpstreamTypeStreamable,
|
||||
}
|
||||
routeRewriteProtocols = map[string]bool{
|
||||
provider.McpSSEProtocol: true,
|
||||
provider.McpStreambleProtocol: true,
|
||||
}
|
||||
mcpServerRewriteProtocols = map[string]bool{
|
||||
provider.McpSSEProtocol: true,
|
||||
}
|
||||
)
|
||||
|
||||
var mcpServerLog = log.RegisterScope("McpServer", "Nacos Mcp Server Watcher process.")
|
||||
@@ -431,7 +451,7 @@ func (w *watcher) getConfigCallback(namespace, group, dataId, data string) {
|
||||
mcpServerLog.Errorf("Unmarshal config data to mcp server error:%v, namespace:%s, groupName:%s, dataId:%s", err, namespace, group, dataId)
|
||||
return
|
||||
}
|
||||
if mcpServer.Protocol == provider.StdioProtocol || mcpServer.Protocol == provider.DubboProtocol || mcpServer.Protocol == provider.McpSSEProtocol {
|
||||
if !supportedProtocols[mcpServer.Protocol] {
|
||||
return
|
||||
}
|
||||
// process mcp service
|
||||
@@ -670,7 +690,9 @@ func (w *watcher) getServiceCallback(server *provider.McpServer, configGroup, da
|
||||
}
|
||||
namespace := server.RemoteServerConfig.ServiceRef.NamespaceId
|
||||
serviceName := server.RemoteServerConfig.ServiceRef.ServiceName
|
||||
path := server.RemoteServerConfig.ExportPath
|
||||
// Higress doesn't care about the MCP export path configured in nacos.
|
||||
// Any path of the mcp server are supported in request routing.
|
||||
path := "/"
|
||||
protocol := server.Protocol
|
||||
host := getNacosServiceFullHost(groupName, namespace, serviceName)
|
||||
|
||||
@@ -708,6 +730,8 @@ func (w *watcher) getServiceCallback(server *provider.McpServer, configGroup, da
|
||||
w.cache.UpdateConfigCache(gvk.ServiceEntry, configKey, se, false)
|
||||
vs := w.buildVirtualServiceForMcpServer(serviceEntry, configGroup, dataId, path, server)
|
||||
w.cache.UpdateConfigCache(gvk.VirtualService, configKey, vs, false)
|
||||
mcpServer := w.buildMcpServerForMcpServer(vs.Spec.(*v1alpha3.VirtualService), configGroup, dataId, path, server)
|
||||
w.cache.UpdateConfigCache(mcpserver.GvkMcpServer, configKey, mcpServer, false)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -735,16 +759,28 @@ func (w *watcher) buildVirtualServiceForMcpServer(serviceentry *v1alpha3.Service
|
||||
if path != "/" {
|
||||
mergePath = mergePath + "/" + strings.TrimPrefix(path, "/")
|
||||
}
|
||||
mergePath = strings.TrimSuffix(mergePath, "/")
|
||||
|
||||
vs := &v1alpha3.VirtualService{
|
||||
Hosts: hosts,
|
||||
Gateways: gateways,
|
||||
Http: []*v1alpha3.HTTPRoute{{
|
||||
Name: routeName,
|
||||
// We need to use both exact and prefix matches here to ensure a proper matching.
|
||||
// Also otherwise, prefix rewrite won't work correctly for Streamable HTTP transport, either.
|
||||
// Example:
|
||||
// Assume mergePath=/mcp/test prefixRewrite=/ requestPath=/mcp/test/abc
|
||||
// If we only use prefix match, the rewritten path will be //abc.
|
||||
Match: []*v1alpha3.HTTPMatchRequest{{
|
||||
Uri: &v1alpha3.StringMatch{
|
||||
MatchType: &v1alpha3.StringMatch_Exact{
|
||||
Exact: mergePath,
|
||||
},
|
||||
},
|
||||
}, {
|
||||
Uri: &v1alpha3.StringMatch{
|
||||
MatchType: &v1alpha3.StringMatch_Prefix{
|
||||
Prefix: mergePath,
|
||||
Prefix: mergePath + "/",
|
||||
},
|
||||
},
|
||||
}},
|
||||
@@ -759,9 +795,9 @@ func (w *watcher) buildVirtualServiceForMcpServer(serviceentry *v1alpha3.Service
|
||||
}},
|
||||
}
|
||||
|
||||
if server.Protocol == provider.McpStreambleProtocol {
|
||||
if routeRewriteProtocols[server.Protocol] {
|
||||
vs.Http[0].Rewrite = &v1alpha3.HTTPRewrite{
|
||||
Uri: path,
|
||||
Uri: "/",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -777,6 +813,49 @@ func (w *watcher) buildVirtualServiceForMcpServer(serviceentry *v1alpha3.Service
|
||||
}
|
||||
}
|
||||
|
||||
func (w *watcher) buildMcpServerForMcpServer(vs *v1alpha3.VirtualService, group, dataId, path string, server *provider.McpServer) *config.Config {
|
||||
if vs == nil {
|
||||
return nil
|
||||
}
|
||||
domains := w.McpServerExportDomains
|
||||
if len(domains) == 0 {
|
||||
domains = []string{"*"}
|
||||
}
|
||||
name := fmt.Sprintf("%s-%s-%s", provider.IstioMcpAutoGeneratedMcpServerName, group, strings.TrimSuffix(dataId, ".json"))
|
||||
httpRoute := vs.Http[0]
|
||||
pathMatchValue := ""
|
||||
for _, match := range httpRoute.Match {
|
||||
if match.Uri != nil && match.Uri.GetExact() != "" {
|
||||
pathMatchValue = match.Uri.GetExact()
|
||||
break
|
||||
}
|
||||
}
|
||||
protocol := server.Protocol
|
||||
|
||||
mcpServer := &mcpserver.McpServer{
|
||||
Name: name,
|
||||
Domains: domains,
|
||||
PathMatchType: mcpserver.PrefixMatchType,
|
||||
PathMatchValue: pathMatchValue,
|
||||
UpstreamType: protocolUpstreamTypeMapping[protocol],
|
||||
}
|
||||
if mcpServerRewriteProtocols[protocol] {
|
||||
mcpServer.EnablePathRewrite = true
|
||||
mcpServer.PathRewritePrefix = "/"
|
||||
}
|
||||
|
||||
mcpServerLog.Debugf("construct mcpserver %v", mcpServer)
|
||||
|
||||
return &config.Config{
|
||||
Meta: config.Meta{
|
||||
GroupVersionKind: mcpserver.GvkMcpServer,
|
||||
Name: name,
|
||||
Namespace: w.namespace,
|
||||
},
|
||||
Spec: mcpServer,
|
||||
}
|
||||
}
|
||||
|
||||
func (w *watcher) generateServiceEntry(host string, services []model.Instance) *v1alpha3.ServiceEntry {
|
||||
portList := make([]*v1alpha3.ServicePort, 0)
|
||||
endpoints := make([]*v1alpha3.WorkloadEntry, 0)
|
||||
@@ -980,3 +1059,13 @@ func isValidIP(ipStr string) bool {
|
||||
ip := net.ParseIP(ipStr)
|
||||
return ip != nil
|
||||
}
|
||||
|
||||
func normalizeRewritePathPrefix(path string) string {
|
||||
if path == "" || path == "/" {
|
||||
return "/"
|
||||
}
|
||||
if path[0] != '/' {
|
||||
path = "/" + path
|
||||
}
|
||||
return strings.TrimSuffix(path, "/")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user