Ver Fonte

feat: list mcp tools (#268)

zijiren há 6 meses atrás
pai
commit
d8d6b55b99

+ 1 - 1
core/controller/mcp/mcp.go

@@ -108,7 +108,7 @@ func prepareEmbedReusingConfig(
 	}
 
 	group := middleware.GetGroup(c)
-	processor := NewReusingParamProcessor(mcpID, group.ID)
+	processor := NewReusingParamProcessor(mcpID, newGroupParams(mcpID, group.ID))
 
 	return processor.ProcessEmbedReusingParams(reusingParams)
 }

+ 31 - 2
core/controller/mcp/publicmcp-group.go

@@ -6,6 +6,7 @@ import (
 
 	"github.com/bytedance/sonic"
 	"github.com/gin-gonic/gin"
+	"github.com/labring/aiproxy/core/common"
 	"github.com/labring/aiproxy/core/controller/utils"
 	"github.com/labring/aiproxy/core/middleware"
 	"github.com/labring/aiproxy/core/model"
@@ -108,6 +109,7 @@ func checkParamsIsFull(params model.Params, reusing map[string]model.ReusingPara
 }
 
 func NewGroupPublicMCPDetailResponse(
+	ctx *gin.Context,
 	host string,
 	mcp model.PublicMCP,
 	groupID string,
@@ -117,6 +119,8 @@ func NewGroupPublicMCPDetailResponse(
 		Hosted:    IsHostedMCP(mcp.Type),
 	}
 
+	testConfig := mcp.TestConfig
+
 	r.Type = ""
 	r.ProxyConfig = nil
 	r.EmbedConfig = nil
@@ -134,7 +138,7 @@ func NewGroupPublicMCPDetailResponse(
 		return r, nil
 	}
 
-	reusingParams, err := model.GetPublicMCPReusingParam(mcp.ID, groupID)
+	reusingParams, err := model.CacheGetPublicMCPReusingParam(mcp.ID, groupID)
 	if err != nil {
 		if !errors.Is(err, gorm.ErrRecordNotFound) {
 			return r, err
@@ -144,6 +148,26 @@ func NewGroupPublicMCPDetailResponse(
 
 	if checkParamsIsFull(r.Params, r.Reusing) {
 		r.Endpoints = NewPublicMCPEndpoint(host, mcp)
+
+		if len(r.Tools) == 0 {
+			tools, err := getPublicMCPTools(ctx.Request.Context(), mcp, r.Params)
+			if err != nil {
+				log := common.GetLogger(ctx)
+				log.Errorf("get public mcp tools error: %s", err.Error())
+			} else {
+				r.Tools = tools
+			}
+		}
+	}
+
+	if len(r.Tools) == 0 && testConfig != nil && testConfig.Enabled {
+		tools, err := getPublicMCPTools(ctx.Request.Context(), mcp, testConfig.Params)
+		if err != nil {
+			log := common.GetLogger(ctx)
+			log.Errorf("get public mcp tools error: %s", err.Error())
+		} else {
+			r.Tools = tools
+		}
 	}
 
 	return r, nil
@@ -215,7 +239,12 @@ func GetGroupPublicMCPByID(c *gin.Context) {
 		return
 	}
 
-	response, err := NewGroupPublicMCPDetailResponse(c.Request.Host, mcp, groupID)
+	response, err := NewGroupPublicMCPDetailResponse(
+		c,
+		c.Request.Host,
+		mcp,
+		groupID,
+	)
 	if err != nil {
 		middleware.ErrorResponse(c, http.StatusInternalServerError, err.Error())
 		return

+ 10 - 11
core/controller/mcp/publicmcp-server.go

@@ -145,7 +145,8 @@ func createProxySSEClient(
 	c *gin.Context,
 	publicMcp *model.PublicMCPCache,
 ) (transport.Interface, error) {
-	url, headers, err := prepareProxyConfig(c, publicMcp)
+	group := middleware.GetGroup(c)
+	url, headers, err := prepareProxyConfig(publicMcp, newGroupParams(publicMcp.ID, group.ID))
 	if err != nil {
 		return nil, err
 	}
@@ -167,7 +168,8 @@ func createProxyStreamableClient(
 	c *gin.Context,
 	publicMcp *model.PublicMCPCache,
 ) (transport.Interface, error) {
-	url, headers, err := prepareProxyConfig(c, publicMcp)
+	group := middleware.GetGroup(c)
+	url, headers, err := prepareProxyConfig(publicMcp, newGroupParams(publicMcp.ID, group.ID))
 	if err != nil {
 		return nil, err
 	}
@@ -186,8 +188,8 @@ func createProxyStreamableClient(
 
 // prepareProxyConfig 准备代理配置
 func prepareProxyConfig(
-	c *gin.Context,
 	publicMcp *model.PublicMCPCache,
+	paramsFunc ParamsFunc,
 ) (string, map[string]string, error) {
 	url, err := url.Parse(publicMcp.ProxyConfig.URL)
 	if err != nil {
@@ -197,15 +199,8 @@ func prepareProxyConfig(
 	headers := make(map[string]string)
 	backendQuery := url.Query()
 
-	// 复制静态配置
-	for k, v := range publicMcp.ProxyConfig.Headers {
-		headers[k] = v
-	}
-
-	// 处理reusing参数
 	if len(publicMcp.ProxyConfig.Reusing) > 0 {
-		group := middleware.GetGroup(c)
-		processor := NewReusingParamProcessor(publicMcp.ID, group.ID)
+		processor := NewReusingParamProcessor(publicMcp.ID, paramsFunc)
 
 		if err := processor.ProcessProxyReusingParams(
 			publicMcp.ProxyConfig.Reusing,
@@ -216,6 +211,10 @@ func prepareProxyConfig(
 		}
 	}
 
+	for k, v := range publicMcp.ProxyConfig.Headers {
+		headers[k] = v
+	}
+
 	url.RawQuery = backendQuery.Encode()
 	return url.String(), headers, nil
 }

+ 167 - 0
core/controller/mcp/publicmcp-tool.go

@@ -0,0 +1,167 @@
+package controller
+
+import (
+	"context"
+	"errors"
+	"time"
+
+	"github.com/bytedance/sonic"
+	"github.com/labring/aiproxy/core/model"
+	mcpservers "github.com/labring/aiproxy/mcp-servers"
+	"github.com/mark3labs/mcp-go/client/transport"
+	"github.com/mark3labs/mcp-go/mcp"
+)
+
+func getPublicMCPTools(
+	ctx context.Context,
+	publicMcp model.PublicMCP,
+	params map[string]string,
+) ([]mcp.Tool, error) {
+	ctx, cancel := context.WithTimeout(ctx, 10*time.Second)
+	defer cancel()
+
+	switch publicMcp.Type {
+	case model.PublicMCPTypeEmbed:
+		return getEmbedMCPTools(ctx, publicMcp, params)
+	case model.PublicMCPTypeOpenAPI:
+		return getOpenAPIMCPTools(ctx, publicMcp)
+	case model.PublicMCPTypeProxySSE:
+		return getProxySSEMCPTools(ctx, publicMcp, params)
+	case model.PublicMCPTypeProxyStreamable:
+		return getProxyStreamableMCPTools(ctx, publicMcp, params)
+	default:
+		return nil, nil
+	}
+}
+
+func getEmbedMCPTools(
+	ctx context.Context,
+	publicMcp model.PublicMCP,
+	params map[string]string,
+) ([]mcp.Tool, error) {
+	if publicMcp.EmbedConfig == nil {
+		return nil, nil
+	}
+
+	server, err := mcpservers.GetMCPServer(publicMcp.ID, publicMcp.EmbedConfig.Init, params)
+	if err != nil {
+		return nil, err
+	}
+
+	return getMCPServerTools(ctx, server)
+}
+
+func getOpenAPIMCPTools(ctx context.Context, publicMcp model.PublicMCP) ([]mcp.Tool, error) {
+	if publicMcp.OpenAPIConfig == nil {
+		return nil, nil
+	}
+
+	server, err := newOpenAPIMCPServer(publicMcp.OpenAPIConfig)
+	if err != nil {
+		return nil, err
+	}
+
+	return getMCPServerTools(ctx, server)
+}
+
+func getProxySSEMCPTools(
+	ctx context.Context,
+	publicMcp model.PublicMCP,
+	params map[string]string,
+) ([]mcp.Tool, error) {
+	if publicMcp.ProxyConfig == nil {
+		return nil, nil
+	}
+
+	url, headers, err := prepareProxyConfig(publicMcp.ToPublicMCPCache(), staticParams(params))
+	if err != nil {
+		return nil, err
+	}
+	client, err := transport.NewSSE(url, transport.WithHeaders(headers))
+	if err != nil {
+		return nil, err
+	}
+	defer client.Close()
+
+	if err := client.Start(ctx); err != nil {
+		return nil, err
+	}
+
+	return getMCPServerTools(ctx, mcpservers.WrapMCPClient2Server(client))
+}
+
+func getProxyStreamableMCPTools(
+	ctx context.Context,
+	publicMcp model.PublicMCP,
+	params map[string]string,
+) ([]mcp.Tool, error) {
+	if publicMcp.ProxyConfig == nil {
+		return nil, nil
+	}
+
+	url, headers, err := prepareProxyConfig(publicMcp.ToPublicMCPCache(), staticParams(params))
+	if err != nil {
+		return nil, err
+	}
+
+	client, err := transport.NewStreamableHTTP(
+		url,
+		transport.WithHTTPHeaders(headers),
+	)
+	if err != nil {
+		return nil, err
+	}
+	defer client.Close()
+
+	if err := client.Start(ctx); err != nil {
+		return nil, err
+	}
+
+	return getMCPServerTools(ctx, mcpservers.WrapMCPClient2Server(client))
+}
+
+func getMCPServerTools(ctx context.Context, server mcpservers.Server) ([]mcp.Tool, error) {
+	requestBytes, err := sonic.Marshal(mcp.JSONRPCRequest{
+		JSONRPC: mcp.JSONRPC_VERSION,
+		ID:      mcp.NewRequestId(1),
+		Request: mcp.Request{
+			Method: string(mcp.MethodToolsList),
+			Params: mcp.RequestParams{},
+		},
+	})
+	if err != nil {
+		return nil, err
+	}
+
+	response := server.HandleMessage(ctx, requestBytes)
+	if response == nil {
+		return nil, errors.New("no response from server")
+	}
+
+	responseBytes, err := sonic.Marshal(response)
+	if err != nil {
+		return nil, err
+	}
+
+	var jsonRPCResponse struct {
+		Result *mcp.ListToolsResult `json:"result"`
+		Error  *struct {
+			Code    int    `json:"code"`
+			Message string `json:"message"`
+		} `json:"error"`
+	}
+
+	if err := sonic.Unmarshal(responseBytes, &jsonRPCResponse); err != nil {
+		return nil, err
+	}
+
+	if jsonRPCResponse.Error != nil {
+		return nil, errors.New(jsonRPCResponse.Error.Message)
+	}
+
+	if jsonRPCResponse.Result == nil {
+		return nil, nil
+	}
+
+	return jsonRPCResponse.Result.Tools, nil
+}

+ 40 - 9
core/controller/mcp/reusing.go

@@ -7,19 +7,50 @@ import (
 	"github.com/labring/aiproxy/core/model"
 )
 
-// ReusingParamProcessor 统一处理reusing参数
-type ReusingParamProcessor struct {
+type ParamsFunc interface {
+	GetParams() (map[string]string, error)
+}
+
+type groupParams struct {
 	mcpID   string
 	groupID string
 }
 
-func NewReusingParamProcessor(mcpID, groupID string) *ReusingParamProcessor {
-	return &ReusingParamProcessor{
+func (g *groupParams) GetParams() (map[string]string, error) {
+	param, err := model.CacheGetPublicMCPReusingParam(g.mcpID, g.groupID)
+	if err != nil {
+		return nil, fmt.Errorf("failed to get reusing params: %w", err)
+	}
+
+	return param.Params, nil
+}
+
+func newGroupParams(mcpID, groupID string) ParamsFunc {
+	return &groupParams{
 		mcpID:   mcpID,
 		groupID: groupID,
 	}
 }
 
+type staticParams map[string]string
+
+func (s staticParams) GetParams() (map[string]string, error) {
+	return s, nil
+}
+
+// ReusingParamProcessor 统一处理reusing参数
+type ReusingParamProcessor struct {
+	mcpID      string
+	paramsFunc ParamsFunc
+}
+
+func NewReusingParamProcessor(mcpID string, paramsFunc ParamsFunc) *ReusingParamProcessor {
+	return &ReusingParamProcessor{
+		mcpID:      mcpID,
+		paramsFunc: paramsFunc,
+	}
+}
+
 // ProcessProxyReusingParams 处理代理类型的reusing参数
 func (p *ReusingParamProcessor) ProcessProxyReusingParams(
 	reusingParams map[string]model.PublicMCPProxyReusingParam,
@@ -30,13 +61,13 @@ func (p *ReusingParamProcessor) ProcessProxyReusingParams(
 		return nil
 	}
 
-	param, err := model.CacheGetPublicMCPReusingParam(p.mcpID, p.groupID)
+	param, err := p.paramsFunc.GetParams()
 	if err != nil {
-		return fmt.Errorf("failed to get reusing params: %w", err)
+		return err
 	}
 
 	for key, config := range reusingParams {
-		value, exists := param.Params[key]
+		value, exists := param[key]
 		if !exists {
 			if config.Required {
 				return fmt.Errorf("required reusing parameter %s is missing", key)
@@ -60,14 +91,14 @@ func (p *ReusingParamProcessor) ProcessEmbedReusingParams(
 		return nil, nil
 	}
 
-	param, err := model.CacheGetPublicMCPReusingParam(p.mcpID, p.groupID)
+	param, err := p.paramsFunc.GetParams()
 	if err != nil {
 		return nil, fmt.Errorf("failed to get reusing params: %w", err)
 	}
 
 	reusingConfig := make(map[string]string)
 	for key, config := range reusingParams {
-		value, exists := param.Params[key]
+		value, exists := param[key]
 		if !exists {
 			if config.Required {
 				return nil, fmt.Errorf("required reusing parameter %s is missing", key)

+ 9 - 9
core/model/cache.go

@@ -614,8 +614,8 @@ type PublicMCPReusingParamCache struct {
 	Params  map[string]string `json:"params"   redis:"p"`
 }
 
-func (p *PublicMCPReusingParam) ToPublicMCPReusingParamCache() *PublicMCPReusingParamCache {
-	return &PublicMCPReusingParamCache{
+func (p *PublicMCPReusingParam) ToPublicMCPReusingParamCache() PublicMCPReusingParamCache {
+	return PublicMCPReusingParamCache{
 		MCPID:   p.MCPID,
 		GroupID: p.GroupID,
 		Params:  p.Params,
@@ -629,7 +629,7 @@ func CacheDeletePublicMCPReusingParam(mcpID, groupID string) error {
 	return common.RedisDel(fmt.Sprintf(PublicMCPReusingParamCacheKey, mcpID, groupID))
 }
 
-func CacheSetPublicMCPReusingParam(param *PublicMCPReusingParamCache) error {
+func CacheSetPublicMCPReusingParam(param PublicMCPReusingParamCache) error {
 	if !common.RedisEnabled {
 		return nil
 	}
@@ -642,9 +642,9 @@ func CacheSetPublicMCPReusingParam(param *PublicMCPReusingParamCache) error {
 	return err
 }
 
-func CacheGetPublicMCPReusingParam(mcpID, groupID string) (*PublicMCPReusingParamCache, error) {
+func CacheGetPublicMCPReusingParam(mcpID, groupID string) (PublicMCPReusingParamCache, error) {
 	if groupID == "" {
-		return &PublicMCPReusingParamCache{
+		return PublicMCPReusingParamCache{
 			MCPID:   mcpID,
 			GroupID: groupID,
 			Params:  make(map[string]string),
@@ -653,14 +653,14 @@ func CacheGetPublicMCPReusingParam(mcpID, groupID string) (*PublicMCPReusingPara
 	if !common.RedisEnabled {
 		param, err := GetPublicMCPReusingParam(mcpID, groupID)
 		if err != nil {
-			return nil, err
+			return PublicMCPReusingParamCache{}, err
 		}
 		return param.ToPublicMCPReusingParamCache(), nil
 	}
 
 	cacheKey := fmt.Sprintf(PublicMCPReusingParamCacheKey, mcpID, groupID)
-	paramCache := &PublicMCPReusingParamCache{}
-	err := common.RDB.HGetAll(context.Background(), cacheKey).Scan(paramCache)
+	paramCache := PublicMCPReusingParamCache{}
+	err := common.RDB.HGetAll(context.Background(), cacheKey).Scan(&paramCache)
 	if err == nil && paramCache.MCPID != "" {
 		return paramCache, nil
 	} else if err != nil && !errors.Is(err, redis.Nil) {
@@ -669,7 +669,7 @@ func CacheGetPublicMCPReusingParam(mcpID, groupID string) (*PublicMCPReusingPara
 
 	param, err := GetPublicMCPReusingParam(mcpID, groupID)
 	if err != nil {
-		return nil, err
+		return PublicMCPReusingParamCache{}, err
 	}
 
 	prc := param.ToPublicMCPReusingParamCache()

+ 11 - 2
go.work.sum

@@ -1,6 +1,7 @@
 cel.dev/expr v0.19.1/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw=
 cel.dev/expr v0.20.0 h1:OunBvVCfvpWlt4dN7zg3FM6TDkzOePe1+foGJ9AXeeI=
 cel.dev/expr v0.20.0/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw=
+cel.dev/expr v0.23.0/go.mod h1:hLPLo1W4QUmuYdA72RBX06QTs6MXw941piREPl3Yfiw=
 cloud.google.com/go v0.120.0 h1:wc6bgG9DHyKqF5/vQvX1CiZrtHnxJjBlKUyF9nP6meA=
 cloud.google.com/go v0.120.0/go.mod h1:/beW32s8/pGRuj4IILWQNd4uuebeT4dkOhKmkfit64Q=
 cloud.google.com/go/longrunning v0.6.6 h1:XJNDo5MUfMM05xK3ewpbSdmt7R2Zw+aQEMbdQR65Rbw=
@@ -10,29 +11,36 @@ cloud.google.com/go/translate v1.10.3/go.mod h1:GW0vC1qvPtd3pgtypCv4k4U8B7EdgK9/
 github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.25.0/go.mod h1:obipzmGjfSjam60XLwGfqUkJsfiheAl+TUjG+4yzyPM=
 github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.26.0 h1:f2Qw/Ehhimh5uO1fayV0QIW7DShEQqhtUfhYc+cBPlw=
 github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.26.0/go.mod h1:2bIszWvQRlJVmJLiuLhukLImRjKPcYdzzsx6darK02A=
+github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.27.0/go.mod h1:yAZHSGnqScoU556rBOVkwLze6WP5N+U11RHuWaGVxwY=
 github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=
 github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
 github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
 github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
 github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.30 h1:x793wxmUWVDhshP8WW2mlnXuFrO4cOd3HLBroh1paFw=
 github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.30/go.mod h1:Jpne2tDnYiFascUEs2AWHJL9Yp7A5ZVy3TNyxaAjD6M=
+github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.31/go.mod h1:nc332eGUU+djP3vrMI6blS0woaCfHTe3KiSQUVTMRq0=
 github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3 h1:eAh2A4b5IzM/lum78bZ590jy36+d/aFLgKF/4Vd1xPE=
 github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3/go.mod h1:0yKJC/kb8sAnmlYa6Zs3QVYqaC8ug2AbnNChv5Ox3uA=
 github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15 h1:dM9/92u2F1JbDaGooxTq18wmmFzbJRfXfVfy96/1CXM=
 github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15/go.mod h1:SwFBy2vjtA0vZbjjaFtfN045boopadnoVPhu4Fv66vY=
+github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.16/go.mod h1:5vkf/Ws0/wgIMJDQbjI4p2op86hNW6Hie5QtebrDgT8=
 github.com/aws/aws-sdk-go-v2/service/sso v1.25.3 h1:1Gw+9ajCV1jogloEv1RRnvfRFia2cL6c9cuKV2Ps+G8=
 github.com/aws/aws-sdk-go-v2/service/sso v1.25.3/go.mod h1:qs4a9T5EMLl/Cajiw2TcbNt2UNo/Hqlyp+GiuG4CFDI=
+github.com/aws/aws-sdk-go-v2/service/sso v1.25.4/go.mod h1:CrtOgCcysxMvrCoHnvNAD7PHWclmoFG78Q2xLK0KKcs=
 github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.1 h1:hXmVKytPfTy5axZ+fYbR5d0cFmC3JvwLm5kM83luako=
 github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.1/go.mod h1:MlYRNmYu/fGPoxBQVvBYr9nyr948aY/WLUvwBMBJubs=
+github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.2/go.mod h1:hwRpqkRxnQ58J9blRDrB4IanlXCpcKmsC83EhG77upg=
 github.com/aws/aws-sdk-go-v2/service/sts v1.33.18/go.mod h1:cQnB8CUnxbMU82JvlqjKR2HBOm3fe9pWorWBza6MBJ4=
 github.com/aws/aws-sdk-go-v2/service/sts v1.33.19 h1:1XuUZ8mYJw9B6lzAkXhqHlJd/XvaX32evhproijJEZY=
 github.com/aws/aws-sdk-go-v2/service/sts v1.33.19/go.mod h1:cQnB8CUnxbMU82JvlqjKR2HBOm3fe9pWorWBza6MBJ4=
+github.com/aws/aws-sdk-go-v2/service/sts v1.33.21/go.mod h1:EhdxtZ+g84MSGrSrHzZiUm9PYiZkrADNja15wtRJSJo=
 github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams=
 github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
 github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg=
 github.com/cncf/xds/go v0.0.0-20241223141626-cff3c89139a3/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8=
 github.com/cncf/xds/go v0.0.0-20250121191232-2f005788dc42 h1:Om6kYQYDUk5wWbT0t0q6pvyM49i9XZAv9dDrkDA7gjk=
 github.com/cncf/xds/go v0.0.0-20250121191232-2f005788dc42/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8=
+github.com/cncf/xds/go v0.0.0-20250326154945-ae57f3c0d45f/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8=
 github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY=
 github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
 github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
@@ -48,6 +56,7 @@ github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWo
 github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
 github.com/go-jose/go-jose/v4 v4.0.4 h1:VsjPI33J0SB9vQM6PLmNjoHqMQNGPiZ0rHL7Ni7Q6/E=
 github.com/go-jose/go-jose/v4 v4.0.4/go.mod h1:NKb5HO1EZccyMpiZNbdUw/14tiXNyUJh188dfnMCAfc=
+github.com/go-jose/go-jose/v4 v4.0.5/go.mod h1:s3P1lRrkT8igV8D9OjyL4WRyHvjB6a4JSllnOrmmBOA=
 github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY=
 github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
 github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
@@ -108,10 +117,10 @@ go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
 go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
 go.opentelemetry.io/contrib/detectors/gcp v1.34.0 h1:JRxssobiPg23otYU5SbWtQC//snGVIM3Tx6QRzlQBao=
 go.opentelemetry.io/contrib/detectors/gcp v1.34.0/go.mod h1:cV4BMFcscUR/ckqLkbfQmF0PRsq8w/lMGzdbCSveBHo=
+go.opentelemetry.io/contrib/detectors/gcp v1.35.0/go.mod h1:qGWP8/+ILwMRIUf9uIVLloR1uo5ZYAslM4O6OqUi1DA=
 golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
 golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0/go.mod h1:S9Xr4PYopiDyqSyp5NjCrhFrqg6A5zA2E/iPHPhqnS8=
 golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
-golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
 golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E=
 golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
 golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
@@ -132,13 +141,13 @@ google.golang.org/genproto/googleapis/bytestream v0.0.0-20250313205543-e70fdf4c4
 google.golang.org/genproto/googleapis/bytestream v0.0.0-20250414145226-207652e42e2e h1:OK8bKvRgTGs7U871RdjtCiRcQJLice8/rZkeoaZgnlc=
 google.golang.org/genproto/googleapis/bytestream v0.0.0-20250414145226-207652e42e2e/go.mod h1:h6yxum/C2qRb4txaZRLDHK8RyS0H/o2oEDeKY4onY/Y=
 google.golang.org/genproto/googleapis/bytestream v0.0.0-20250512202823-5a2f75b736a9/go.mod h1:h6yxum/C2qRb4txaZRLDHK8RyS0H/o2oEDeKY4onY/Y=
+google.golang.org/genproto/googleapis/bytestream v0.0.0-20250528174236-200df99c418a/go.mod h1:h6yxum/C2qRb4txaZRLDHK8RyS0H/o2oEDeKY4onY/Y=
 lukechampine.com/uint128 v1.3.0 h1:cDdUVfRwDUDovz610ABgFD17nXD4/uDgVHl2sC3+sbo=
 lukechampine.com/uint128 v1.3.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk=
 modernc.org/cc/v3 v3.41.0 h1:QoR1Sn3YWlmA1T4vLaKZfawdVtSiGx8H+cEojbC7v1Q=
 modernc.org/cc/v3 v3.41.0/go.mod h1:Ni4zjJYJ04CDOhG7dn640WGfwBzfE0ecX8TyMB0Fv0Y=
 modernc.org/ccgo/v3 v3.16.15 h1:KbDR3ZAVU+wiLyMESPtbtE/Add4elztFyfsWoNTgxS0=
 modernc.org/ccgo/v3 v3.16.15/go.mod h1:yT7B+/E2m43tmMOT51GMoM98/MtHIcQQSleGnddkUNI=
-modernc.org/fileutil v1.3.3/go.mod h1:HxmghZSZVAz/LXcMNwZPA/DRrQZEVP9VX0V4LQGQFOc=
 nullprogram.com/x/optparse v1.0.0 h1:xGFgVi5ZaWOnYdac2foDT3vg0ZZC9ErXFV57mr4OHrI=
 rsc.io/pdf v0.1.1 h1:k1MczvYDUvJBe93bYd7wrZLLUEcLZAuF824/I4e5Xr4=
 rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=