Compare commits

..

6 Commits

Author SHA1 Message Date
johnlanni
48b220453b release 2.1.2 2025-04-29 20:53:50 +08:00
mirror
489a800868 add: add mcp-context7 descriptions (#2149) 2025-04-29 20:44:00 +08:00
澄潭
60c9f21e1c When the service source type is nacos3, if mcpserver is turned off, then the discovery mechanism of nacos2 will be enabled (#2150) 2025-04-29 17:29:52 +08:00
Jingze
ab73f21017 fix: make mcp server redis client config based (#2145)
Co-authored-by: daijingze_mac <18373118@buaa.edu.cn>
2025-04-29 14:27:48 +08:00
EricaLiu
806563298b fix : when nacos push empty service instance list, should skip generate (#2144) 2025-04-29 11:38:51 +08:00
github-actions[bot]
02fabbb35f Update helm translated README.zh.md (#2141) 2025-04-29 09:28:20 +08:00
15 changed files with 280 additions and 241 deletions

View File

@@ -1 +1 @@
v2.1.2-rc.1
v2.1.2

View File

@@ -1,5 +1,5 @@
apiVersion: v2
appVersion: 2.1.2-rc.1
appVersion: 2.1.2
description: Helm chart for deploying higress gateways
icon: https://higress.io/img/higress_logo_small.png
home: http://higress.io/
@@ -15,4 +15,4 @@ dependencies:
repository: "file://../redis"
version: 0.0.1
type: application
version: 2.1.2-rc.1
version: 2.1.2

View File

@@ -1,9 +1,9 @@
dependencies:
- name: higress-core
repository: file://../core
version: 2.1.2-rc.1
version: 2.1.2
- name: higress-console
repository: https://higress.io/helm-charts/
version: 2.1.2
digest: sha256:0933dd97b646ae2e1740ccfb6606793b088abeeeb03f687a94c02fe34f227d87
generated: "2025-04-28T20:34:11.26913+08:00"
digest: sha256:7612de239141ca0d27400f7d5b9a786acd98826f511e2e3ed65ccd9d2c9f1700
generated: "2025-04-29T20:52:39.996652+08:00"

View File

@@ -1,5 +1,5 @@
apiVersion: v2
appVersion: 2.1.2-rc.1
appVersion: 2.1.2
description: Helm chart for deploying Higress gateways
icon: https://higress.io/img/higress_logo_small.png
home: http://higress.io/
@@ -12,9 +12,9 @@ sources:
dependencies:
- name: higress-core
repository: "file://../core"
version: 2.1.2-rc.1
version: 2.1.2
- name: higress-console
repository: "https://higress.io/helm-charts/"
version: 2.1.2
type: application
version: 2.1.2-rc.1
version: 2.1.2

View File

@@ -1,8 +1,8 @@
## Higress for Kubernetes
Higress 是基于阿里巴巴内部网关实践构建的云原生 API 网关。
Higress 是基于阿里巴巴内部网关实践的云原生 API 网关。
依托 Istio 和 EnvoyHigress 实现了流量网关、微服务网关和安全网关三重架构的融合,从而大降低了部署、运维成本。
通过基于 Istio 和 EnvoyHigress 实现了流量网关、微服务网关和安全网关三重网关架构的集成,从而大降低了部署、运维成本。
## 设置仓库信息
@@ -13,7 +13,7 @@ helm repo update
## 安装
`higress` 为发布名称安装 chart
使用名为 `higress` 的版本来安装 chart
```console
helm install higress -n higress-system higress.io/higress --create-namespace --render-subchart-notes
@@ -21,168 +21,60 @@ helm install higress -n higress-system higress.io/higress --create-namespace --r
## 卸载
卸载/删除 higress 部署:
卸载删除 higress 部署:
```console
helm delete higress -n higress-system
```
该命令会除与 chart 相关的所有 Kubernetes 组件并删除发
该命令会除与 chart 相关的所有 Kubernetes 组件并删除发行版
## 参数
## 值
## 配置
| 键 | 类型 | 默认值 | 描述 |
|-----|------|---------|-------------|
| clusterName | 字符串 | `""` | |
| controller.affinity | 对象 | `{}` | |
| controller.automaticHttps.email | 字符串 | `""` | |
| controller.automaticHttps.enabled | 布尔值 | `true` | |
| controller.autoscaling.enabled | 布尔值 | `false` | |
| controller.autoscaling.maxReplicas | 整数 | `5` | |
| controller.autoscaling.minReplicas | 整数 | `1` | |
| controller.autoscaling.targetCPUUtilizationPercentage | 整数 | `80` | |
| controller.env | 对象 | `{}` | |
| controller.hub | 字符串 | `"higress-registry.cn-hangzhou.cr.aliyuncs.com/higress"` | |
| controller.image | 字符串 | `"higress"` | |
| controller.imagePullSecrets | 列表 | `[]` | |
| controller.labels | 对象 | `{}` | |
| controller.name | 字符串 | `"higress-controller"` | |
| controller.nodeSelector | 对象 | `{}` | |
| controller.podAnnotations | 对象 | `{}` | |
| controller.podSecurityContext | 对象 | `{}` | |
| controller.ports[0].name | 字符串 | `"http"` | |
| controller.ports[0].port | 整数 | `8888` | |
| controller.ports[0].protocol | 字符串 | `"TCP"` | |
| controller.ports[0].targetPort | 整数 | `8888` | |
| controller.ports[1].name | 字符串 | `"http-solver"` | |
| controller.ports[1].port | 整数 | `8889` | |
| controller.ports[1].protocol | 字符串 | `"TCP"` | |
| controller.ports[1].targetPort | 整数 | `8889` | |
| controller.ports[2].name | 字符串 | `"grpc"` | |
| controller.ports[2].port | 整数 | `15051` | |
| controller.ports[2].protocol | 字符串 | `"TCP"` | |
| controller.ports[2].targetPort | 整数 | `15051` | |
| controller.probe.httpGet.path | 字符串 | `"/ready"` | |
| controller.probe.httpGet.port | 整数 | `8888` | |
| controller.probe.initialDelaySeconds | 整数 | `1` | |
| controller.probe.periodSeconds | 整数 | `3` | |
| controller.probe.timeoutSeconds | 整数 | `5` | |
| controller.rbac.create | 布尔值 | `true` | |
| controller.replicas | 整数 | `1` | Higress Controller 的 Pod 数量 |
| controller.resources.limits.cpu | 字符串 | `"1000m"` | |
| controller.resources.limits.memory | 字符串 | `"2048Mi"` | |
| controller.resources.requests.cpu | 字符串 | `"500m"` | |
| controller.resources.requests.memory | 字符串 | `"2048Mi"` | |
| controller.securityContext | 对象 | `{}` | |
| controller.service.type | 字符串 | `"ClusterIP"` | |
| controller.serviceAccount.annotations | 对象 | `{}` | 添加到服务账户的注解 |
| controller.serviceAccount.create | 布尔值 | `true` | 指定是否创建服务账户 |
| controller.serviceAccount.name | 字符串 | `""` | 如果未设置且 create 为 true则使用 fullname 模板生成名称 |
| controller.tag | 字符串 | `""` | |
| controller.tolerations | 列表 | `[]` | |
| downstream | 对象 | `{"connectionBufferLimits":32768,"http2":{"initialConnectionWindowSize":1048576,"initialStreamWindowSize":65535,"maxConcurrentStreams":100},"idleTimeout":180,"maxRequestHeadersKb":60,"routeTimeout":0}` | 下游配置设置 |
| gateway.affinity | 对象 | `{}` | |
| gateway.annotations | 对象 | `{}` | 应用到所有资源的注解 |
| gateway.autoscaling.enabled | 布尔值 | `false` | |
| gateway.autoscaling.maxReplicas | 整数 | `5` | |
| gateway.autoscaling.minReplicas | 整数 | `1` | |
| gateway.autoscaling.targetCPUUtilizationPercentage | 整数 | `80` | |
| gateway.containerSecurityContext | 字符串 | `nil` | |
| gateway.env | 对象 | `{}` | Pod 环境变量 |
| gateway.hostNetwork | 布尔值 | `false` | |
| gateway.httpPort | 整数 | `80` | |
| gateway.httpsPort | 整数 | `443` | |
| gateway.hub | 字符串 | `"higress-registry.cn-hangzhou.cr.aliyuncs.com/higress"` | |
| gateway.image | 字符串 | `"gateway"` | |
| gateway.kind | 字符串 | `"Deployment"` | 使用 `DaemonSet``Deployment` |
| gateway.labels | 对象 | `{}` | 应用到所有资源的标签 |
| gateway.metrics.enabled | 布尔值 | `false` | 如果为 true则为网关创建 PodMonitor 或 VMPodScrape |
| gateway.metrics.honorLabels | 布尔值 | `false` | |
| gateway.metrics.interval | 字符串 | `""` | |
| gateway.metrics.metricRelabelConfigs | 列表 | `[]` | 用于 operator.victoriametrics.com/v1beta1.VMPodScrape |
| gateway.metrics.metricRelabelings | 列表 | `[]` | 用于 monitoring.coreos.com/v1.PodMonitor |
| gateway.metrics.provider | 字符串 | `"monitoring.coreos.com"` | CustomResourceDefinition 的提供者组名,可以是 monitoring.coreos.com 或 operator.victoriametrics.com |
| gateway.metrics.rawSpec | 对象 | `{}` | 更多原始的 podMetricsEndpoints 规范 |
| gateway.metrics.relabelConfigs | 列表 | `[]` | |
| gateway.metrics.relabelings | 列表 | `[]` | |
| gateway.metrics.scrapeTimeout | 字符串 | `""` | |
| gateway.name | 字符串 | `"higress-gateway"` | |
| gateway.networkGateway | 字符串 | `""` | 如果指定,网关将作为给定网络的网络网关。 |
| gateway.nodeSelector | 对象 | `{}` | |
| gateway.podAnnotations."prometheus.io/path" | 字符串 | `"/stats/prometheus"` | |
| gateway.podAnnotations."prometheus.io/port" | 字符串 | `"15020"` | |
| gateway.podAnnotations."prometheus.io/scrape" | 字符串 | `"true"` | |
| gateway.podAnnotations."sidecar.istio.io/inject" | 字符串 | `"false"` | |
| gateway.rbac.enabled | 布尔值 | `true` | 如果启用,将创建角色以启用从网关访问证书。当使用 http://gateway-api.org/ 时不需要。 |
| gateway.readinessFailureThreshold | 整数 | `30` | 指示准备失败前的连续失败探测次数。 |
| gateway.readinessInitialDelaySeconds | 整数 | `1` | 准备探测的初始延迟秒数。 |
| gateway.readinessPeriodSeconds | 整数 | `2` | 准备探测之间的间隔。 |
| gateway.readinessSuccessThreshold | 整数 | `1` | 指示准备成功前的连续成功探测次数。 |
| gateway.readinessTimeoutSeconds | 整数 | `3` | 准备探测的超时秒数 |
| gateway.replicas | 整数 | `2` | Higress Gateway 的 Pod 数量 |
| gateway.resources.limits.cpu | 字符串 | `"2000m"` | |
| gateway.resources.limits.memory | 字符串 | `"2048Mi"` | |
| gateway.resources.requests.cpu | 字符串 | `"2000m"` | |
| gateway.resources.requests.memory | 字符串 | `"2048Mi"` | |
| gateway.revision | 字符串 | `""` | 修订声明此网关属于哪个修订 |
| gateway.rollingMaxSurge | 字符串 | `"100%"` | |
| gateway.rollingMaxUnavailable | 字符串 | `"25%"` | |
| gateway.securityContext | 字符串 | `nil` | 定义 Pod 的安全上下文。如果未设置,将自动设置为绑定到端口 80 和 443 所需的最小权限。在 Kubernetes 1.22+ 上,这只需要 `net.ipv4.ip_unprivileged_port_start` 系统调用。 |
| gateway.service.annotations | 对象 | `{}` | |
| gateway.service.externalTrafficPolicy | 字符串 | `""` | |
| gateway.service.loadBalancerClass | 字符串 | `""` | |
| gateway.service.loadBalancerIP | 字符串 | `""` | |
| gateway.service.loadBalancerSourceRanges | 列表 | `[]` | |
| gateway.service.ports[0].name | 字符串 | `"http2"` | |
| gateway.service.ports[0].port | 整数 | `80` | |
| gateway.service.ports[0].protocol | 字符串 | `"TCP"` | |
| gateway.service.ports[0].targetPort | 整数 | `80` | |
| gateway.service.ports[1].name | 字符串 | `"https"` | |
| gateway.service.ports[1].port | 整数 | `443` | |
| gateway.service.ports[1].protocol | 字符串 | `"TCP"` | |
| gateway.service.ports[1].targetPort | 整数 | `443` | |
| gateway.service.type | 字符串 | `"LoadBalancer"` | 服务类型。设置为 "None" 以完全禁用服务 |
| gateway.serviceAccount.annotations | 对象 | `{}` | 添加到服务账户的注解 |
| gateway.serviceAccount.create | 布尔值 | `true` | 如果设置,将创建服务账户。否则,使用默认值 |
| gateway.serviceAccount.name | 字符串 | `""` | 要使用的服务账户名称。如果未设置,则使用发布名称 |
| gateway.tag | 字符串 | `""` | |
| gateway.tolerations | 列表 | `[]` | |
| gateway.unprivilegedPortSupported | 字符串 | `nil` | |
| global.autoscalingv2API | 布尔值 | `true` | 是否使用 autoscaling/v2 模板进行 HPA 设置,仅供内部使用,用户不应配置。 |
| global.caAddress | 字符串 | `""` | 自定义的 CA 地址,用于为集群中的 Pod 检索证书。CSR 客户端(如 Istio Agent 和 ingress gateways可以使用此地址指定 CA 端点。如果未明确设置,则默认为 Istio 发现地址。 |
| global.caName | 字符串 | `""` | 工作负载证书的 CA 名称。例如,当 caName=GkeWorkloadCertificate 时GKE 工作负载证书将用作工作负载的证书。默认值为 "",当 caName="" 时CA 将通过其他机制(如环境变量 CA_PROVIDER配置。 |
| global.configCluster | 布尔值 | `false` | 将远程集群配置为外部 istiod 的配置集群。 |
| global.defaultPodDisruptionBudget | 对象 | `{"enabled":false}` | 为控制平面启用 Pod 中断预算,用于确保 Istio 控制平面组件逐步升级或恢复。 |
| global.defaultResources | 对象 | `{"requests":{"cpu":"10m"}}` | 应用于所有部署的最小请求资源集,以便 Horizontal Pod Autoscaler 能够正常工作(如果设置)。每个组件可以通过在相关部分添加自己的资源块并设置所需的资源值来覆盖这些默认值。 |
| global.defaultUpstreamConcurrencyThreshold | 整数 | `10000` | |
| global.disableAlpnH2 | 布尔值 | `false` | 是否在 ALPN 中禁用 HTTP/2 |
| global.enableGatewayAPI | 布尔值 | `false` | 如果为 trueHigress Controller 还将监控 Gateway API 资源 |
| global.enableH3 | 布尔值 | `false` | |
| global.enableIPv6 | 布尔值 | `false` | |
| global.enableIstioAPI | 布尔值 | `true` | 如果为 trueHigress Controller 还将监控 istio 资源 |
| global.enableLDSCache | 布尔值 | `true` | |
| global.enableProxyProtocol | 布尔值 | `false` | |
| global.enablePushAllMCPClusters | 布尔值 | `true` | |
| global.enableSRDS | 布尔值 | `true` | |
| global.enableStatus | 布尔值 | `true` | 如果为 trueHigress Controller 将更新 Ingress 资源的状态字段。从 Nginx Ingress 迁移时,为了避免 Ingress 对象的状态字段被覆盖,需要将此参数设置为 false以便 Higress 不会将入口 IP 写入相应 Ingress 对象的状态字段。 |
| global.externalIstiod | 布尔值 | `false` | 配置由外部 istiod 控制的远程集群数据平面。当设置为 true 时,本地不部署 istiod仅启用其他发现 chart 的子集。 |
| global.hostRDSMergeSubset | 布尔值 | `false` | |
| global.hub | 字符串 | `"higress-registry.cn-hangzhou.cr.aliyuncs.com/higress"` | Istio 镜像的默认仓库。发布版本发布到 docker hub 的 'istio' 项目下。来自 prow 的开发构建位于 gcr.io |
| global.imagePullPolicy | 字符串 | `""` | 如果不需要默认行为,则指定镜像拉取策略。默认行为:最新镜像将始终拉取,否则 IfNotPresent。 |
| global.imagePullSecrets | 列表 | `[]` | 所有 ServiceAccount 的 ImagePullSecrets用于引用此 ServiceAccount 的 Pod 拉取任何镜像的同一命名空间中的秘密列表。对于不使用 ServiceAccount 的组件(即 grafana、servicegraph、tracingImagePullSecrets 将添加到相应的 Deployment(StatefulSet) 对象中。对于配置了私有 docker 注册表的任何集群,必须设置。 |
| global.ingressClass | 字符串 | `"higress"` | IngressClass 过滤 higress controller 监听的 ingress 资源。默认的 ingress class 是 higress。有一些特殊情况用于特殊的 ingress class。1. 当 ingress class 设置为 nginx 时higress controller 将监听带有 nginx ingress class 或没有任何 ingress class 的 ingress 资源。2. 当 ingress class 设置为空时higress controller 将监听 k8s 集群中的所有 ingress 资源。 |
| global.istioNamespace | 字符串 | `"istio-system"` | 用于定位 istiod。 |
| global.istiod | 对象 | `{"enableAnalysis":false}` | 默认在主分支中启用以最大化测试。 |
| global.jwtPolicy | 字符串 | `"third-party-jwt"` | 配置验证 JWT 的策略。目前支持两个选项:"third-party-jwt" 和 "first-party-jwt"。 |
| global.kind | 布尔值 | `false` | |
| global.liteMetrics | 布尔值 | `false` | |
| global.local | 布尔值 | `false` | 当部署到本地集群kind 集群)时,将此设置为 true。 |
| global.logAsJson | 布尔值 | `false` | |
| global.logging | 对象 | `{"level":"default:info"}` | 以逗号分隔的每个范围的最小日志级别,格式为 <scope>:<level>,<scope>:<level> 控制平面根据组件不同有不同的范围,但可以配置所有组件的默认日志级别 如果为空,将使用代码中配置的默认范围和级别 |
| global.meshID | 字符串 | `""` | 如果网格管理员未指定值Istio 将使用网格的信任域的值。最佳实践是选择一个合适的信任域值。 |
| global.meshNetworks | 对象 | `{}` | |
| global.mountMtlsCerts | 布尔值 | `false` | 使用用户指定的、挂载的密钥和证书用于 Pilot 和工作负载。 |
| global.multiCluster.clusterName | 字符串 | `""` | 应设置为此安装运行的集群的名称。这是为了正确标记代理的 sidecar 注入所必需的 |
| global.multiCluster.enabled | 布尔值 | `true` | 设置为 true 以通过各自的 ingressgateway 服务连接两个 kubernetes 集群,当每个集群中的 Pod 无法直接相互通信时。
| 键 | 类型 | 默认值 | 描述 |
|------|------|---------|-------------|
| clusterName | string | `""` | |
| controller.affinity | object | `{}` | |
| controller.automaticHttps.email | string | `""` | |
| controller.automaticHttps.enabled | bool | `true` | |
| controller.autoscaling.enabled | bool | `false` | |
| controller.autoscaling.maxReplicas | int | `5` | |
| controller.autoscaling.minReplicas | int | `1` | |
| controller.autoscaling.targetCPUUtilizationPercentage | int | `80` | |
| controller.env | object | `{}` | |
| controller.hub | string | `"higress-registry.cn-hangzhou.cr.aliyuncs.com/higress"` | |
| controller.image | string | `"higress"` | |
| controller.imagePullSecrets | list | `[]` | |
| controller.labels | object | `{}` | |
| controller.name | string | `"higress-controller"` | |
| controller.nodeSelector | object | `{}` | |
| controller.podAnnotations | object | `{}` | |
| controller.podLabels | object | `{}` | 应用到 pod 上的标签 |
| controller.podSecurityContext | object | `{}` | |
| controller.ports[0].name | string | `"http"` | |
| controller.ports[0].port | int | `8888` | |
| controller.ports[0].protocol | string | `"TCP"` | |
| controller.ports[0].targetPort | int | `8888` | |
| controller.probe.httpGet.path | string | `"/ready"` | |
| controller.probe.httpGet.port | int | `8888` | |
| controller.probe.initialDelaySeconds | int | `1` | |
| controller.probe.periodSeconds | int | `3` | |
| controller.probe.timeoutSeconds | int | `5` | |
| controller.rbac.create | bool | `true` | |
| controller.replicas | int | `1` | Higress Controller pods 的数量 |
| controller.resources.limits.cpu | string | `"1000m"` | |
| controller.resources.limits.memory | string | `"2048Mi"` | |
| controller.resources.requests.cpu | string | `"500m"` | |
| controller.resources.requests.memory | string | `"2048Mi"` | |
| gateway.metrics.enabled | bool | `false` | 如果为 true则为gateway创建PodMonitor或VMPodScrape |
| gateway.metrics.provider | string | `monitoring.coreos.com` | CustomResourceDefinition 的提供商组名,可以是 monitoring.coreos.com 或 operator.victoriametrics.com |
| gateway.readinessFailureThreshold | int | `30` | 成功进行探针测试前连续失败探针的最大次数。 |
| global MeshNetworks | object | `{}` | |
| global.tracer.datadog.address | string | `"$(HOST_IP):8126"` | 提交给 Datadog agent 的 Host:Port 。|
| redis.redis.persistence.enabled | bool | `false` | 启用 Redis 持久性,默认为 false |
| redis.redis.persistence.size | string | `"1Gi"` | Persistent Volume 大小 |
| redis.redis.service.port | int | `6379` | Exporter service 端口 |
| tracing.skywalking.port | int | `11800` | |
| upstream.connectionBufferLimits | int | `10485760` | 上游连接缓冲限制(字节)|

View File

@@ -104,7 +104,6 @@ func (p *Parser) Parse(any *anypb.Any, callbacks api.ConfigCallbackHandler) (int
conf.servers = append(conf.servers, &SSEServerWrapper{
BaseServer: common.NewSSEServer(serverInstance,
common.WithRedisClient(common.GlobalRedisClient),
common.WithSSEEndpoint(fmt.Sprintf("%s%s", serverPath, mcp_session.GlobalSSEPathSuffix)),
common.WithMessageEndpoint(serverPath)),
DomainList: serverDomainList,

View File

@@ -9,8 +9,6 @@ import (
"github.com/go-redis/redis/v8"
)
var GlobalRedisClient *RedisClient
type RedisConfig struct {
address string
username string
@@ -74,9 +72,10 @@ func NewRedisClient(config *RedisConfig) (*RedisClient, error) {
// Ping the Redis server to check the connection
pong, err := client.Ping(context.Background()).Result()
if err != nil {
return nil, fmt.Errorf("failed to connect to Redis: %w", err)
api.LogErrorf("Failed to connect to Redis: %v", err)
} else {
api.LogDebugf("Connected to Redis: %s", pong)
}
api.LogDebugf("Connected to Redis: %s", pong)
ctx, cancel := context.WithCancel(context.Background())
@@ -85,7 +84,7 @@ func NewRedisClient(config *RedisConfig) (*RedisClient, error) {
crypto, err = NewCrypto(config.secret)
if err != nil {
cancel()
return nil, err
api.LogWarnf("Failed to initialize redis crypto: %v", err)
}
}
@@ -105,7 +104,7 @@ func NewRedisClient(config *RedisConfig) (*RedisClient, error) {
// keepAlive periodically checks Redis connection and attempts to reconnect if needed
func (r *RedisClient) keepAlive() {
ticker := time.NewTicker(30 * time.Second)
ticker := time.NewTicker(5 * time.Second)
defer ticker.Stop()
for {

View File

@@ -210,7 +210,7 @@ func (s *SSEServer) HandleMessage(w http.ResponseWriter, r *http.Request, body j
var status int
// Only send response if there is one (not for notifications)
if response != nil {
if sessionID != "" && s.redisClient != nil {
if sessionID != ""{
w.WriteHeader(http.StatusAccepted)
status = http.StatusAccepted
} else {

View File

@@ -25,12 +25,13 @@ type config struct {
enableUserLevelServer bool
rateLimitConfig *handler.MCPRatelimitConfig
defaultServer *common.SSEServer
redisClient *common.RedisClient
}
func (c *config) Destroy() {
if common.GlobalRedisClient != nil {
if c.redisClient != nil {
api.LogDebug("Closing Redis client")
common.GlobalRedisClient.Close()
c.redisClient.Close()
}
}
@@ -63,10 +64,11 @@ func (p *Parser) Parse(any *anypb.Any, callbacks api.ConfigCallbackHandler) (int
redisClient, err := common.NewRedisClient(redisConfig)
if err != nil {
return nil, fmt.Errorf("failed to initialize RedisClient: %w", err)
api.LogErrorf("Failed to initialize Redis client: %w", err)
} else {
api.LogDebug("Redis client initialized")
}
common.GlobalRedisClient = redisClient
api.LogDebug("Redis client initialized")
conf.redisClient = redisClient
} else {
api.LogDebug("Redis configuration not provided, running without Redis")
}
@@ -74,7 +76,7 @@ func (p *Parser) Parse(any *anypb.Any, callbacks api.ConfigCallbackHandler) (int
enableUserLevelServer, ok := v.AsMap()["enable_user_level_server"].(bool)
if !ok {
enableUserLevelServer = false
if common.GlobalRedisClient == nil {
if conf.redisClient == nil {
return nil, fmt.Errorf("redis configuration is not provided, enable_user_level_server is true")
}
}
@@ -137,7 +139,7 @@ func FilterFactory(c interface{}, callbacks api.FilterCallbackHandler) api.Strea
callbacks: callbacks,
config: conf,
stopChan: make(chan struct{}),
mcpConfigHandler: handler.NewMCPConfigHandler(common.GlobalRedisClient, callbacks),
mcpRatelimitHandler: handler.NewMCPRatelimitHandler(common.GlobalRedisClient, callbacks, conf.rateLimitConfig),
mcpConfigHandler: handler.NewMCPConfigHandler(conf.redisClient, callbacks),
mcpRatelimitHandler: handler.NewMCPRatelimitHandler(conf.redisClient, callbacks, conf.rateLimitConfig),
}
}

View File

@@ -108,7 +108,7 @@ func (f *filter) DecodeHeaders(header api.RequestHeaderMap, endStream bool) api.
f.config.defaultServer = common.NewSSEServer(common.NewMCPServer(DefaultServerName, Version),
common.WithSSEEndpoint(GlobalSSEPathSuffix),
common.WithMessageEndpoint(strings.TrimSuffix(url.ParsedURL.Path, GlobalSSEPathSuffix)),
common.WithRedisClient(common.GlobalRedisClient))
common.WithRedisClient(f.config.redisClient))
f.serverName = f.config.defaultServer.GetServerName()
body := "SSE connection create"
f.callbacks.DecoderFilterCallbacks().SendLocalReply(http.StatusOK, body, nil, 0, "")
@@ -165,7 +165,7 @@ func (f *filter) EncodeHeaders(header api.ResponseHeaderMap, endStream bool) api
return api.Continue
}
if f.serverName != "" {
if common.GlobalRedisClient != nil {
if f.config.redisClient != nil {
header.Set("Content-Type", "text/event-stream")
header.Set("Cache-Control", "no-cache")
header.Set("Connection", "keep-alive")
@@ -188,12 +188,12 @@ func (f *filter) EncodeData(buffer api.BufferInstance, endStream bool) api.Statu
if !endStream {
return api.StopAndBuffer
}
if f.proxyURL != nil && common.GlobalRedisClient != nil {
if f.proxyURL != nil && f.config.redisClient != nil {
sessionID := f.proxyURL.Query().Get("sessionId")
if sessionID != "" {
channel := common.GetSSEChannelName(sessionID)
eventData := fmt.Sprintf("event: message\ndata: %s\n\n", buffer.String())
publishErr := common.GlobalRedisClient.Publish(channel, eventData)
publishErr := f.config.redisClient.Publish(channel, eventData)
if publishErr != nil {
api.LogErrorf("Failed to publish wasm mcp server message to Redis: %v", publishErr)
}
@@ -201,7 +201,7 @@ func (f *filter) EncodeData(buffer api.BufferInstance, endStream bool) api.Statu
}
if f.serverName != "" {
if common.GlobalRedisClient != nil {
if f.config.redisClient != nil {
// handle default server
buffer.Reset()
f.config.defaultServer.HandleSSE(f.callbacks, f.stopChan)

View File

@@ -0,0 +1,49 @@
# Context7 MCP Server
An implementation of the Model Context Protocol (MCP) server that integrates [Context7](https://context7.com), providing up-to-date, version-specific documentation and code examples.
Source Code: [https://github.com/upstash/context7](https://github.com/upstash/context7)
## Features
- Get up-to-date, version-specific documentation
- Extract real, working code examples from source
- Provide concise, relevant information without filler
- Free for personal use
- Integration with your MCP server and tools
## Usage Guide
### Generate SSE URL
On the MCP Server interface, log in and enter the API-KEY to generate the URL.
### Configure MCP Client
On the user's MCP Client interface, add the generated SSE URL to the MCP Server list.
```json
"mcpServers": {
"context7": {
"url": "https://mcp.higress.ai/mcp-context7/{generate_key}",
}
}
```
### Available Tools
#### resolve-library-id
Resolves a general package name into a Context7-compatible library ID. This is a required first step before using the get-library-docs tool.
Parameters:
- query: Library name to search for and retrieve a Context7-compatible library ID (required)
#### get-library-docs
Fetches up-to-date documentation for a library. You must call resolve-library-id first to obtain the exact Context7-compatible library ID.
Parameters:
- folders: Folders filter for organizing documentation
- libraryId: Unique identifier of the library (required)
- tokens: Maximum number of tokens to return (default: 5000)
- topic: Specific topic within the documentation
- type: Type of documentation to retrieve (currently only "txt" supported)

View File

@@ -0,0 +1,49 @@
# Context7 MCP Server
一个集成了[Context7](https://context7.com)的模型上下文协议MCP服务器实现提供最新、版本特定的文档和代码示例。
源码地址:[https://github.com/upstash/context7](https://github.com/upstash/context7)
## 功能
- 获取最新、版本特定的文档
- 从源码中提取真实可用的代码示例
- 提供简洁、相关的信息,无冗余内容
- 支持个人免费使用
- 与MCP服务器和工具集成
## 使用教程
### 生成 SSE URL
在 MCP Server 界面,登录后输入 API-KEY生成URL。
### 配置 MCP Client
在用户的 MCP Client 界面,将生成的 SSE URL添加到 MCP Server列表中。
```json
"mcpServers": {
"context7": {
"url": "https://mcp.higress.ai/mcp-context7/{generate_key}",
}
}
```
### 可用工具
#### resolve-library-id
用于将通用包名解析为Context7兼容的库ID是使用get-library-docs工具获取文档的必要前置步骤。
参数说明:
- query: 要搜索的库名称用于获取Context7兼容的库ID (必填)
#### get-library-docs
获取库的最新文档。使用前必须先调用resolve-library-id工具获取Context7兼容的库ID。
参数说明:
- folders: 用于组织文档的文件夹过滤器
- libraryId: 库的唯一标识符 (必填)
- tokens: 返回的最大token数默认5000
- topic: 文档中的特定主题
- type: 要检索的文档类型,目前仅支持"txt"

View File

@@ -0,0 +1,55 @@
server:
name: context7-mcp-server
tools:
- name: resolve-library-id
description: Required first step - Resolves a general package name into a Context7-compatible library ID. Must be called before using 'get-library-docs' to retrieve a valid Context7-compatible library ID.
args:
- name: query
description: Library name to search for and retrieve a Context7-compatible library ID.
type: string
required: true
position: query
requestTemplate:
url: https://context7.com/api/v1/search
method: GET
responseTemplate:
body: |
{{- range $index, $item := .results }}
## 结果 {{add $index 1}}
- **id**: {{ $item.id }}
- **title**: {{ $item.title }}
- **description**: {{ $item.description }}
{{- end }}
- name: get-library-docs
description: Fetches up-to-date documentation for a library. You must call 'resolve-library-id' first to obtain the exact Context7-compatible library ID required to use this tool.
args:
- name: folders
description: Folders filter for organizing documentation
type: string
position: query
- name: libraryId
description: Unique identifier of the library
type: string
required: true
position: path
- name: tokens
description: Maximum number of tokens to return
type: integer
position: query
default: 5000
- name: topic
description: Specific topic within the documentation
type: string
position: query
- name: type
description: Type of documentation to retrieve
type: string
position: query
enum: ["txt"]
requestTemplate:
url: https://context7.com/api/v1{libraryId}
method: GET
headers:
- key: X-Context7-Source
value: server

View File

@@ -111,9 +111,10 @@ func NewWatcher(cache memory.Cache, opts ...WatcherOption) (provider.Watcher, er
opt(w)
}
if w.NacosNamespace == "" {
w.NacosNamespace = w.NacosNamespaceId
}
// The nacos mcp server uses these restricted namespaces and groups, and may be adjusted in the future.
w.NacosNamespace = "nacos-default-mcp"
w.NacosNamespaceId = w.NacosNamespace
w.NacosGroups = []string{"mcp-server"}
mcpServerLog.Infof("new nacos mcp server watcher with config Name:%s", w.Name)
@@ -177,32 +178,6 @@ func WithNacosSecretKey(nacosSecretKey string) WatcherOption {
}
}
func WithNacosNamespaceId(nacosNamespaceId string) WatcherOption {
return func(w *watcher) {
if nacosNamespaceId == "" {
w.NacosNamespaceId = "nacos-default-mcp"
} else {
w.NacosNamespaceId = nacosNamespaceId
}
}
}
func WithNacosNamespace(nacosNamespace string) WatcherOption {
return func(w *watcher) {
w.NacosNamespace = nacosNamespace
}
}
func WithNacosGroups(nacosGroups []string) WatcherOption {
return func(w *watcher) {
if len(nacosGroups) == 0 {
w.NacosGroups = []string{"mcp-server"}
} else {
w.NacosGroups = nacosGroups
}
}
}
func WithNacosRefreshInterval(refreshInterval int64) WatcherOption {
return func(w *watcher) {
if refreshInterval < int64(DefaultRefreshIntervalLimit) {
@@ -686,14 +661,17 @@ func (w *watcher) getServiceCallback(server *provider.McpServer, configGroup, da
serviceName := server.RemoteServerConfig.ServiceRef.ServiceName
path := server.RemoteServerConfig.ExportPath
protocol := server.Protocol
host := getNacosServiceFullHost(groupName, namespace, serviceName)
return func(services []model.Instance) {
defer w.UpdateService()
configKey := strings.Join([]string{w.Name, w.NacosNamespace, configGroup, dataId}, DefaultJoiner)
host := getNacosServiceFullHost(groupName, namespace, serviceName)
mcpServerLog.Infof("callback for %s/%s, serviceName : %s", configGroup, dataId, host)
configKey := strings.Join([]string{w.Name, w.NacosNamespace, configGroup, dataId}, DefaultJoiner)
if len(services) == 0 {
mcpServerLog.Errorf("callback for %s return empty service instance list, skip generate config", host)
return
}
serviceEntry := w.generateServiceEntry(host, services)
se := &config.Config{
@@ -964,7 +942,8 @@ func (w *watcher) Stop() {
}
mcpServerLog.Infof("stop all service nameing client")
for _, client := range w.serviceCache {
client.Stop()
// TODO: This is a temporary implementation because of a bug in the nacos-go-sdk, which causes a block when stoping.
go client.Stop()
}
w.isStop = true

View File

@@ -187,25 +187,40 @@ func (r *Reconciler) generateWatcherFromRegistryConfig(registry *apiv1.RegistryC
nacosv2.WithAuthOption(authOption),
)
case string(Nacos3):
watcher, err = mcpserver.NewWatcher(
r.Cache,
mcpserver.WithType(registry.Type),
mcpserver.WithName(registry.Name),
mcpserver.WithNacosAddressServer(registry.NacosAddressServer),
mcpserver.WithDomain(registry.Domain),
mcpserver.WithPort(registry.Port),
mcpserver.WithNacosAccessKey(registry.NacosAccessKey),
mcpserver.WithNacosSecretKey(registry.NacosSecretKey),
mcpserver.WithNacosNamespaceId(registry.NacosNamespaceId),
mcpserver.WithNacosNamespace(registry.NacosNamespace),
mcpserver.WithNacosGroups(registry.NacosGroups),
mcpserver.WithNacosRefreshInterval(registry.NacosRefreshInterval),
mcpserver.WithMcpExportDomains(registry.McpServerExportDomains),
mcpserver.WithMcpBaseUrl(registry.McpServerBaseUrl),
mcpserver.WithEnableMcpServer(registry.EnableMCPServer),
mcpserver.WithClusterId(r.clusterId),
mcpserver.WithNamespace(r.namespace),
)
if registry.EnableMCPServer.GetValue() {
watcher, err = mcpserver.NewWatcher(
r.Cache,
mcpserver.WithType(registry.Type),
mcpserver.WithName(registry.Name),
mcpserver.WithNacosAddressServer(registry.NacosAddressServer),
mcpserver.WithDomain(registry.Domain),
mcpserver.WithPort(registry.Port),
mcpserver.WithNacosAccessKey(registry.NacosAccessKey),
mcpserver.WithNacosSecretKey(registry.NacosSecretKey),
mcpserver.WithNacosRefreshInterval(registry.NacosRefreshInterval),
mcpserver.WithMcpExportDomains(registry.McpServerExportDomains),
mcpserver.WithMcpBaseUrl(registry.McpServerBaseUrl),
mcpserver.WithEnableMcpServer(registry.EnableMCPServer),
mcpserver.WithClusterId(r.clusterId),
mcpserver.WithNamespace(r.namespace),
)
} else {
watcher, err = nacosv2.NewWatcher(
r.Cache,
nacosv2.WithType(registry.Type),
nacosv2.WithName(registry.Name),
nacosv2.WithNacosAddressServer(registry.NacosAddressServer),
nacosv2.WithDomain(registry.Domain),
nacosv2.WithPort(registry.Port),
nacosv2.WithNacosAccessKey(registry.NacosAccessKey),
nacosv2.WithNacosSecretKey(registry.NacosSecretKey),
nacosv2.WithNacosNamespaceId(registry.NacosNamespaceId),
nacosv2.WithNacosNamespace(registry.NacosNamespace),
nacosv2.WithNacosGroups(registry.NacosGroups),
nacosv2.WithNacosRefreshInterval(registry.NacosRefreshInterval),
nacosv2.WithAuthOption(authOption),
)
}
case string(Zookeeper):
watcher, err = zookeeper.NewWatcher(
r.Cache,