Browse Source

feat: embed mcp type (#260)

* feat: embed mcp type

* fix: streamable proxy

* fix: ci lint
zijiren 7 months ago
parent
commit
e5d271fca3

+ 92 - 35
core/controller/mcp/embedmcp.go

@@ -2,6 +2,7 @@ package controller
 
 
 import (
 import (
 	"context"
 	"context"
+	"errors"
 	"fmt"
 	"fmt"
 	"maps"
 	"maps"
 	"net/http"
 	"net/http"
@@ -45,6 +46,16 @@ func newEmbedMCPConfigTemplates(templates mcpservers.ConfigTemplates) EmbedMCPCo
 	return emcpTemplates
 	return emcpTemplates
 }
 }
 
 
+func newEmbedMCPProxyConfigTemplates(
+	templates mcpservers.ProxyConfigTemplates,
+) EmbedMCPConfigTemplates {
+	emcpTemplates := make(EmbedMCPConfigTemplates, len(templates))
+	for key, template := range templates {
+		emcpTemplates[key] = newEmbedMCPConfigTemplate(template.ConfigTemplate)
+	}
+	return emcpTemplates
+}
+
 type EmbedMCP struct {
 type EmbedMCP struct {
 	ID              string                    `json:"id"`
 	ID              string                    `json:"id"`
 	Enabled         bool                      `json:"enabled"`
 	Enabled         bool                      `json:"enabled"`
@@ -65,17 +76,25 @@ func newEmbedMCP(
 	embedConfig *model.MCPEmbeddingConfig,
 	embedConfig *model.MCPEmbeddingConfig,
 ) *EmbedMCP {
 ) *EmbedMCP {
 	emcp := &EmbedMCP{
 	emcp := &EmbedMCP{
-		ID:              mcp.ID,
-		Enabled:         enabled,
-		Name:            mcp.Name,
-		Readme:          mcp.Readme,
-		ReadmeURL:       mcp.ReadmeURL,
-		ReadmeCN:        mcp.ReadmeCN,
-		ReadmeCNURL:     mcp.ReadmeCNURL,
-		GitHubURL:       mcp.GitHubURL,
-		Tags:            mcp.Tags,
-		ConfigTemplates: newEmbedMCPConfigTemplates(mcp.ConfigTemplates),
-		EmbedConfig:     embedConfig,
+		ID:          mcp.ID,
+		Enabled:     enabled,
+		Name:        mcp.Name,
+		Readme:      mcp.Readme,
+		ReadmeURL:   mcp.ReadmeURL,
+		ReadmeCN:    mcp.ReadmeCN,
+		ReadmeCNURL: mcp.ReadmeCNURL,
+		GitHubURL:   mcp.GitHubURL,
+		Tags:        mcp.Tags,
+		EmbedConfig: embedConfig,
+	}
+	if len(mcp.ConfigTemplates) != 0 {
+		emcp.ConfigTemplates = newEmbedMCPConfigTemplates(mcp.ConfigTemplates)
+	}
+	if len(mcp.ProxyConfigTemplates) != 0 {
+		emcp.ConfigTemplates = newEmbedMCPProxyConfigTemplates(mcp.ProxyConfigTemplates)
+	}
+	if emcp.ConfigTemplates == nil {
+		emcp.ConfigTemplates = make(EmbedMCPConfigTemplates)
 	}
 	}
 	return emcp
 	return emcp
 }
 }
@@ -181,41 +200,79 @@ func GetEmbedConfig(
 	return embedConfig, nil
 	return embedConfig, nil
 }
 }
 
 
+func GetProxyConfig(
+	proxyConfigType mcpservers.ProxyConfigTemplates,
+	initConfig map[string]string,
+) (*model.PublicMCPProxyConfig, error) {
+	if len(proxyConfigType) == 0 {
+		return nil, errors.New("proxy config type is empty")
+	}
+
+	config := &model.PublicMCPProxyConfig{
+		Querys:  make(map[string]string),
+		Headers: make(map[string]string),
+		Reusing: make(map[string]model.PublicMCPProxyReusingParam),
+	}
+
+	for key, param := range proxyConfigType {
+		value := initConfig[key]
+		if value == "" {
+			value = param.Default
+		}
+
+		switch param.Type {
+		case model.ParamTypeURL:
+			if value == "" {
+				return nil, fmt.Errorf("url parameter %s is required", key)
+			}
+			config.URL = value
+		case model.ParamTypeHeader:
+			if value != "" {
+				config.Headers[key] = value
+			}
+		case model.ParamTypeQuery:
+			if value != "" {
+				config.Querys[key] = value
+			}
+		default:
+			return nil, fmt.Errorf("unsupported proxy param type: %s", param.Type)
+		}
+	}
+
+	if config.URL == "" {
+		return nil, errors.New("url is required in proxy config")
+	}
+
+	return config, nil
+}
+
 func ToPublicMCP(
 func ToPublicMCP(
 	e mcpservers.McpServer,
 	e mcpservers.McpServer,
 	initConfig map[string]string,
 	initConfig map[string]string,
 	enabled bool,
 	enabled bool,
 ) (*model.PublicMCP, error) {
 ) (*model.PublicMCP, error) {
-	embedConfig, err := GetEmbedConfig(e.ConfigTemplates, initConfig)
-	if err != nil {
-		return nil, err
-	}
-	pmcp := &model.PublicMCP{
-		ID:            e.ID,
-		Name:          e.Name,
-		LogoURL:       e.LogoURL,
-		Description:   e.Description,
-		DescriptionCN: e.DescriptionCN,
-		Readme:        e.Readme,
-		ReadmeURL:     e.ReadmeURL,
-		ReadmeCN:      e.ReadmeCN,
-		ReadmeCNURL:   e.ReadmeCNURL,
-		GithubURL:     e.GitHubURL,
-		Tags:          e.Tags,
-		EmbedConfig:   embedConfig,
+	pmcp := e.PublicMCP
+	switch e.Type {
+	case model.PublicMCPTypeEmbed:
+		embedConfig, err := GetEmbedConfig(e.ConfigTemplates, initConfig)
+		if err != nil {
+			return nil, err
+		}
+		pmcp.EmbedConfig = embedConfig
+	case model.PublicMCPTypeProxySSE, model.PublicMCPTypeProxyStreamable:
+		proxyConfig, err := GetProxyConfig(e.ProxyConfigTemplates, initConfig)
+		if err != nil {
+			return nil, err
+		}
+		pmcp.ProxyConfig = proxyConfig
+	default:
 	}
 	}
 	if enabled {
 	if enabled {
 		pmcp.Status = model.PublicMCPStatusEnabled
 		pmcp.Status = model.PublicMCPStatusEnabled
 	} else {
 	} else {
 		pmcp.Status = model.PublicMCPStatusDisabled
 		pmcp.Status = model.PublicMCPStatusDisabled
 	}
 	}
-	switch e.Type {
-	case mcpservers.McpTypeEmbed:
-		pmcp.Type = model.PublicMCPTypeEmbed
-	case mcpservers.McpTypeDocs:
-		pmcp.Type = model.PublicMCPTypeDocs
-	}
-	return pmcp, nil
+	return &pmcp, nil
 }
 }
 
 
 // SaveEmbedMCP godoc
 // SaveEmbedMCP godoc

+ 13 - 1
core/mcpproxy/stateless-streamable.go

@@ -83,9 +83,21 @@ func (s *StreamableHTTPServer) handlePost(w http.ResponseWriter, r *http.Request
 		return
 		return
 	}
 	}
 
 
+	jsonBody, err := sonic.Marshal(response)
+	if err != nil {
+		s.writeJSONRPCError(
+			w,
+			nil,
+			mcp.INTERNAL_ERROR,
+			fmt.Sprintf("marshal response body error: %v", err),
+		)
+		return
+	}
+
 	w.Header().Set("Content-Type", "application/json")
 	w.Header().Set("Content-Type", "application/json")
+	w.Header().Set("Content-Length", strconv.Itoa(len(jsonBody)))
 	w.WriteHeader(http.StatusOK)
 	w.WriteHeader(http.StatusOK)
-	_ = sonic.ConfigDefault.NewEncoder(w).Encode(response)
+	_, _ = w.Write(jsonBody)
 }
 }
 
 
 func (s *StreamableHTTPServer) handleGet(w http.ResponseWriter, _ *http.Request) {
 func (s *StreamableHTTPServer) handleGet(w http.ResponseWriter, _ *http.Request) {

+ 23 - 14
core/mcpproxy/streamable.go

@@ -92,7 +92,7 @@ func (p *StreamableProxy) handleGetRequest(w http.ResponseWriter, r *http.Reques
 	}
 	}
 
 
 	// Extract the real backend session ID from the stored URL
 	// Extract the real backend session ID from the stored URL
-	parts := strings.Split(backendInfo, "?sessionId=")
+	parts := strings.Split(backendInfo, "|sessionId=")
 	if len(parts) > 1 {
 	if len(parts) > 1 {
 		req.Header.Set(headerKeySessionID, parts[1])
 		req.Header.Set(headerKeySessionID, parts[1])
 	}
 	}
@@ -102,6 +102,8 @@ func (p *StreamableProxy) handleGetRequest(w http.ResponseWriter, r *http.Reques
 		req.Header.Set(name, value)
 		req.Header.Set(name, value)
 	}
 	}
 
 
+	req.Header.Set("Content-Type", r.Header.Get("Content-Type"))
+
 	//nolint:bodyclose
 	//nolint:bodyclose
 	resp, err := http.DefaultClient.Do(req)
 	resp, err := http.DefaultClient.Do(req)
 	if err != nil {
 	if err != nil {
@@ -175,24 +177,32 @@ func (p *StreamableProxy) handlePostRequest(w http.ResponseWriter, r *http.Reque
 		return
 		return
 	}
 	}
 
 
+	// Extract the real backend session ID from the stored URL
+	parts := strings.Split(backendInfo, "|sessionId=")
+	if len(parts) != 2 {
+		http.Error(w, "Invalid or expired session ID", http.StatusNotFound)
+		return
+	}
+	backend := parts[0]
+	sessionID := parts[1]
+
 	// Create a request to the backend
 	// Create a request to the backend
-	req, err := http.NewRequestWithContext(r.Context(), http.MethodPost, backendInfo, r.Body)
+	req, err := http.NewRequestWithContext(r.Context(), http.MethodPost, backend, r.Body)
 	if err != nil {
 	if err != nil {
 		http.Error(w, "Failed to create backend request", http.StatusInternalServerError)
 		http.Error(w, "Failed to create backend request", http.StatusInternalServerError)
 		return
 		return
 	}
 	}
 
 
-	// Extract the real backend session ID from the stored URL
-	parts := strings.Split(backendInfo, "?sessionId=")
-	if len(parts) > 1 {
-		req.Header.Set(headerKeySessionID, parts[1])
-	}
-
 	// Add any additional headers
 	// Add any additional headers
 	for name, value := range p.headers {
 	for name, value := range p.headers {
 		req.Header.Set(name, value)
 		req.Header.Set(name, value)
 	}
 	}
 
 
+	req.Header.Set(headerKeySessionID, sessionID)
+
+	req.Header.Set("Accept", "application/json, text/event-stream")
+	req.Header.Set("Content-Type", r.Header.Get("Content-Type"))
+
 	//nolint:bodyclose
 	//nolint:bodyclose
 	resp, err := http.DefaultClient.Do(req)
 	resp, err := http.DefaultClient.Do(req)
 	if err != nil {
 	if err != nil {
@@ -274,7 +284,7 @@ func (p *StreamableProxy) handleDeleteRequest(w http.ResponseWriter, r *http.Req
 	}
 	}
 
 
 	// Extract the real backend session ID from the stored URL
 	// Extract the real backend session ID from the stored URL
-	parts := strings.Split(backendInfo, "?sessionId=")
+	parts := strings.Split(backendInfo, "|sessionId=")
 	if len(parts) > 1 {
 	if len(parts) > 1 {
 		req.Header.Set(headerKeySessionID, parts[1])
 		req.Header.Set(headerKeySessionID, parts[1])
 	}
 	}
@@ -323,6 +333,9 @@ func (p *StreamableProxy) proxyInitialOrNoSessionRequest(w http.ResponseWriter,
 		req.Header.Set(name, value)
 		req.Header.Set(name, value)
 	}
 	}
 
 
+	req.Header.Set("Accept", "application/json, text/event-stream")
+	req.Header.Set("Content-Type", r.Header.Get("Content-Type"))
+
 	//nolint:bodyclose
 	//nolint:bodyclose
 	resp, err := http.DefaultClient.Do(req)
 	resp, err := http.DefaultClient.Do(req)
 	if err != nil {
 	if err != nil {
@@ -340,11 +353,7 @@ func (p *StreamableProxy) proxyInitialOrNoSessionRequest(w http.ResponseWriter,
 		// Store the mapping between our proxy session ID and the backend endpoint with its session
 		// Store the mapping between our proxy session ID and the backend endpoint with its session
 		// ID
 		// ID
 		backendURL := p.backend
 		backendURL := p.backend
-		if strings.Contains(backendURL, "?") {
-			backendURL += "&sessionId=" + backendSessionID
-		} else {
-			backendURL += "?sessionId=" + backendSessionID
-		}
+		backendURL += "|sessionId=" + backendSessionID
 		p.store.Set(proxySessionID, backendURL)
 		p.store.Set(proxySessionID, backendURL)
 
 
 		// Replace the backend session ID with our proxy session ID in the response
 		// Replace the backend session ID with our proxy session ID in the response

+ 6 - 5
core/model/publicmcp.go

@@ -34,11 +34,12 @@ const (
 	PublicMCPTypeEmbed           PublicMCPType = "mcp_embed"
 	PublicMCPTypeEmbed           PublicMCPType = "mcp_embed"
 )
 )
 
 
-type ParamType string
+type ProxyParamType string
 
 
 const (
 const (
-	ParamTypeHeader ParamType = "header"
-	ParamTypeQuery  ParamType = "query"
+	ParamTypeURL    ProxyParamType = "url"
+	ParamTypeHeader ProxyParamType = "header"
+	ParamTypeQuery  ProxyParamType = "query"
 )
 )
 
 
 type ReusingParam struct {
 type ReusingParam struct {
@@ -54,7 +55,7 @@ type MCPPrice struct {
 
 
 type PublicMCPProxyReusingParam struct {
 type PublicMCPProxyReusingParam struct {
 	ReusingParam
 	ReusingParam
-	Type ParamType `json:"type"`
+	Type ProxyParamType `json:"type"`
 }
 }
 
 
 type PublicMCPProxyConfig struct {
 type PublicMCPProxyConfig struct {
@@ -132,7 +133,7 @@ type PublicMCP struct {
 	Type                   PublicMCPType           `gorm:"index"                         json:"type"`
 	Type                   PublicMCPType           `gorm:"index"                         json:"type"`
 	Description            string                  `                                     json:"description"`
 	Description            string                  `                                     json:"description"`
 	DescriptionCN          string                  `                                     json:"description_cn"`
 	DescriptionCN          string                  `                                     json:"description_cn"`
-	GithubURL              string                  `                                     json:"github_url"`
+	GitHubURL              string                  `                                     json:"github_url"`
 	Readme                 string                  `gorm:"type:text"                     json:"readme"`
 	Readme                 string                  `gorm:"type:text"                     json:"readme"`
 	ReadmeCN               string                  `gorm:"type:text"                     json:"readme_cn"`
 	ReadmeCN               string                  `gorm:"type:text"                     json:"readme_cn"`
 	ReadmeURL              string                  `                                     json:"readme_url"`
 	ReadmeURL              string                  `                                     json:"readme_url"`

+ 2 - 1
mcp-servers/12306/init.go

@@ -4,6 +4,7 @@ package train12306
 import (
 import (
 	_ "embed"
 	_ "embed"
 
 
+	"github.com/labring/aiproxy/core/model"
 	mcpservers "github.com/labring/aiproxy/mcp-servers"
 	mcpservers "github.com/labring/aiproxy/mcp-servers"
 )
 )
 
 
@@ -18,7 +19,7 @@ func init() {
 		mcpservers.NewMcp(
 		mcpservers.NewMcp(
 			"12306",
 			"12306",
 			"12306 Train Ticket Query",
 			"12306 Train Ticket Query",
-			mcpservers.McpTypeEmbed,
+			model.PublicMCPTypeEmbed,
 			mcpservers.WithNewServerFunc(NewServer),
 			mcpservers.WithNewServerFunc(NewServer),
 			mcpservers.WithGitHubURL(
 			mcpservers.WithGitHubURL(
 				"https://github.com/Joooook/12306-mcp",
 				"https://github.com/Joooook/12306-mcp",

+ 5 - 2
mcp-servers/aiproxy-openapi/init.go

@@ -1,6 +1,9 @@
 package aiproxyopenapi
 package aiproxyopenapi
 
 
-import mcpservers "github.com/labring/aiproxy/mcp-servers"
+import (
+	"github.com/labring/aiproxy/core/model"
+	mcpservers "github.com/labring/aiproxy/mcp-servers"
+)
 
 
 // need import in mcpregister/init.go
 // need import in mcpregister/init.go
 func init() {
 func init() {
@@ -8,7 +11,7 @@ func init() {
 		mcpservers.NewMcp(
 		mcpservers.NewMcp(
 			"aiproxy-openapi",
 			"aiproxy-openapi",
 			"AI Proxy OpenAPI",
 			"AI Proxy OpenAPI",
-			mcpservers.McpTypeEmbed,
+			model.PublicMCPTypeEmbed,
 			mcpservers.WithNewServerFunc(NewServer),
 			mcpservers.WithNewServerFunc(NewServer),
 			mcpservers.WithConfigTemplates(configTemplates),
 			mcpservers.WithConfigTemplates(configTemplates),
 			mcpservers.WithDescription(
 			mcpservers.WithDescription(

+ 2 - 1
mcp-servers/alipay/init.go

@@ -3,6 +3,7 @@ package alipay
 import (
 import (
 	_ "embed"
 	_ "embed"
 
 
+	"github.com/labring/aiproxy/core/model"
 	mcpservers "github.com/labring/aiproxy/mcp-servers"
 	mcpservers "github.com/labring/aiproxy/mcp-servers"
 )
 )
 
 
@@ -15,7 +16,7 @@ func init() {
 		mcpservers.NewMcp(
 		mcpservers.NewMcp(
 			"alipay",
 			"alipay",
 			"Alipay",
 			"Alipay",
-			mcpservers.McpTypeDocs,
+			model.PublicMCPTypeDocs,
 			mcpservers.WithTags([]string{"pay"}),
 			mcpservers.WithTags([]string{"pay"}),
 			mcpservers.WithDescription(
 			mcpservers.WithDescription(
 				"支付宝 MCP Server,让你可以轻松将支付宝开放平台提供的交易创建、查询、退款等能力集成到你的 LLM 应用中,并进一步创建具备支付能力的智能工具。",
 				"支付宝 MCP Server,让你可以轻松将支付宝开放平台提供的交易创建、查询、退款等能力集成到你的 LLM 应用中,并进一步创建具备支付能力的智能工具。",

+ 5 - 2
mcp-servers/amap/init.go

@@ -1,6 +1,9 @@
 package amap
 package amap
 
 
-import mcpservers "github.com/labring/aiproxy/mcp-servers"
+import (
+	"github.com/labring/aiproxy/core/model"
+	mcpservers "github.com/labring/aiproxy/mcp-servers"
+)
 
 
 // need import in mcpregister/init.go
 // need import in mcpregister/init.go
 func init() {
 func init() {
@@ -8,7 +11,7 @@ func init() {
 		mcpservers.NewMcp(
 		mcpservers.NewMcp(
 			"amap",
 			"amap",
 			"AMAP",
 			"AMAP",
-			mcpservers.McpTypeEmbed,
+			model.PublicMCPTypeEmbed,
 			mcpservers.WithNewServerFunc(NewServer),
 			mcpservers.WithNewServerFunc(NewServer),
 			mcpservers.WithConfigTemplates(configTemplates),
 			mcpservers.WithConfigTemplates(configTemplates),
 			mcpservers.WithTags([]string{"map"}),
 			mcpservers.WithTags([]string{"map"}),

+ 2 - 1
mcp-servers/baidu-map/init.go

@@ -3,6 +3,7 @@ package baidumap
 import (
 import (
 	_ "embed"
 	_ "embed"
 
 
+	"github.com/labring/aiproxy/core/model"
 	mcpservers "github.com/labring/aiproxy/mcp-servers"
 	mcpservers "github.com/labring/aiproxy/mcp-servers"
 )
 )
 
 
@@ -14,7 +15,7 @@ func init() {
 		mcpservers.NewMcp(
 		mcpservers.NewMcp(
 			"baidu-map",
 			"baidu-map",
 			"Baidu Map",
 			"Baidu Map",
-			mcpservers.McpTypeEmbed,
+			model.PublicMCPTypeEmbed,
 			mcpservers.WithNewServerFunc(NewServer),
 			mcpservers.WithNewServerFunc(NewServer),
 			mcpservers.WithGitHubURL(
 			mcpservers.WithGitHubURL(
 				"https://github.com/baidu-maps/mcp",
 				"https://github.com/baidu-maps/mcp",

+ 2 - 1
mcp-servers/bingcn/init.go

@@ -3,6 +3,7 @@ package bingcn
 import (
 import (
 	_ "embed"
 	_ "embed"
 
 
+	"github.com/labring/aiproxy/core/model"
 	mcpservers "github.com/labring/aiproxy/mcp-servers"
 	mcpservers "github.com/labring/aiproxy/mcp-servers"
 )
 )
 
 
@@ -14,7 +15,7 @@ func init() {
 		mcpservers.NewMcp(
 		mcpservers.NewMcp(
 			"bing-cn-search",
 			"bing-cn-search",
 			"Bing CN Search",
 			"Bing CN Search",
-			mcpservers.McpTypeEmbed,
+			model.PublicMCPTypeEmbed,
 			mcpservers.WithNewServerFunc(NewServer),
 			mcpservers.WithNewServerFunc(NewServer),
 			mcpservers.WithGitHubURL(
 			mcpservers.WithGitHubURL(
 				"https://github.com/yan5236/bing-cn-mcp-server",
 				"https://github.com/yan5236/bing-cn-mcp-server",

+ 2 - 1
mcp-servers/fetch/init.go

@@ -3,6 +3,7 @@ package fetch
 import (
 import (
 	_ "embed"
 	_ "embed"
 
 
+	"github.com/labring/aiproxy/core/model"
 	mcpservers "github.com/labring/aiproxy/mcp-servers"
 	mcpservers "github.com/labring/aiproxy/mcp-servers"
 )
 )
 
 
@@ -17,7 +18,7 @@ func init() {
 		mcpservers.NewMcp(
 		mcpservers.NewMcp(
 			"fetch",
 			"fetch",
 			"Fetch",
 			"Fetch",
-			mcpservers.McpTypeEmbed,
+			model.PublicMCPTypeEmbed,
 			mcpservers.WithNewServerFunc(NewServer),
 			mcpservers.WithNewServerFunc(NewServer),
 			mcpservers.WithGitHubURL(
 			mcpservers.WithGitHubURL(
 				"https://github.com/modelcontextprotocol/servers/tree/main/src/fetch",
 				"https://github.com/modelcontextprotocol/servers/tree/main/src/fetch",

+ 2 - 1
mcp-servers/firecrawl/init.go

@@ -3,6 +3,7 @@ package firecrawl
 import (
 import (
 	_ "embed"
 	_ "embed"
 
 
+	"github.com/labring/aiproxy/core/model"
 	mcpservers "github.com/labring/aiproxy/mcp-servers"
 	mcpservers "github.com/labring/aiproxy/mcp-servers"
 )
 )
 
 
@@ -17,7 +18,7 @@ func init() {
 		mcpservers.NewMcp(
 		mcpservers.NewMcp(
 			"firecrawl",
 			"firecrawl",
 			"Firecrawl",
 			"Firecrawl",
-			mcpservers.McpTypeEmbed,
+			model.PublicMCPTypeEmbed,
 			mcpservers.WithNewServerFunc(NewServer),
 			mcpservers.WithNewServerFunc(NewServer),
 			mcpservers.WithGitHubURL(
 			mcpservers.WithGitHubURL(
 				"https://github.com/mendableai/firecrawl-mcp-server",
 				"https://github.com/mendableai/firecrawl-mcp-server",

+ 2 - 1
mcp-servers/flomo/init.go

@@ -3,6 +3,7 @@ package flomo
 import (
 import (
 	_ "embed"
 	_ "embed"
 
 
+	"github.com/labring/aiproxy/core/model"
 	mcpservers "github.com/labring/aiproxy/mcp-servers"
 	mcpservers "github.com/labring/aiproxy/mcp-servers"
 )
 )
 
 
@@ -17,7 +18,7 @@ func init() {
 		mcpservers.NewMcp(
 		mcpservers.NewMcp(
 			"flomo",
 			"flomo",
 			"Flomo",
 			"Flomo",
-			mcpservers.McpTypeEmbed,
+			model.PublicMCPTypeEmbed,
 			mcpservers.WithNewServerFunc(NewServer),
 			mcpservers.WithNewServerFunc(NewServer),
 			mcpservers.WithGitHubURL(
 			mcpservers.WithGitHubURL(
 				"https://github.com/chatmcp/mcp-server-flomo",
 				"https://github.com/chatmcp/mcp-server-flomo",

+ 17 - 0
mcp-servers/gezhe/README.cn.md

@@ -0,0 +1,17 @@
+# Gezhe-MCP-server
+
+## 简介
+
+歌者 PPT MCP server, 可以通过话题生成 PPT
+
+### Tools
+
+1. `generate_ppt_by_topic`
+   - 输入:
+     - `topic` (string): 话题名称
+   - 返回: 预览链接
+
+## 使用指引
+
+1. 访问并登录 <https://pro.gezhe.com/settings>
+2. 进入「设置-MCP 服务器」页面,复制页面中提供的 URL 地址

+ 17 - 0
mcp-servers/gezhe/README.md

@@ -0,0 +1,17 @@
+# Gezhe-MCP-server
+
+## Introduction
+
+Gezhe PPT MCP server, can generate PPTs based on topics.
+
+### Tools
+
+1. `generate_ppt_by_topic`
+   - Input:
+     - `topic` (string): Topic name
+   - Returns: Preview link
+
+## Usage Guide
+
+1. Visit and log in to <https://pro.gezhe.com/settings>
+2. Go to the "Settings - MCP Server" page and copy the URL provided on the page.

+ 32 - 0
mcp-servers/gezhe/init.go

@@ -0,0 +1,32 @@
+package gezhe
+
+import (
+	_ "embed"
+
+	"github.com/labring/aiproxy/core/model"
+	mcpservers "github.com/labring/aiproxy/mcp-servers"
+)
+
+//go:embed README.md
+var readme string
+
+//go:embed README.cn.md
+var readmeCN string
+
+// need import in mcpregister/init.go
+func init() {
+	mcpservers.Register(
+		mcpservers.NewMcp(
+			"gezhe",
+			"Gezhe",
+			model.PublicMCPTypeProxyStreamable,
+			mcpservers.WithProxyConfigType(configTemplates),
+			mcpservers.WithTags([]string{"map"}),
+			mcpservers.WithDescription(
+				"可以通过话题生成 PPT",
+			),
+			mcpservers.WithReadme(readme),
+			mcpservers.WithReadmeCN(readmeCN),
+		),
+	)
+}

+ 30 - 0
mcp-servers/gezhe/main.go

@@ -0,0 +1,30 @@
+package gezhe
+
+import (
+	"github.com/labring/aiproxy/core/model"
+	mcpservers "github.com/labring/aiproxy/mcp-servers"
+)
+
+const defaultURL = "https://mcp.gezhe.com/mcp"
+
+var configTemplates = mcpservers.ProxyConfigTemplates{
+	"API_KEY": {
+		ConfigTemplate: mcpservers.ConfigTemplate{
+			Name:        "API_KEY",
+			Required:    mcpservers.ConfigRequiredTypeInitOrReusingOnly,
+			Example:     "bx7Qt1BLbxRq...",
+			Description: "The key of the Gezhe MCP server: https://pro.gezhe.com/settings",
+		},
+		Type: model.ParamTypeQuery,
+	},
+
+	"url": {
+		ConfigTemplate: mcpservers.ConfigTemplate{
+			Name:        "URL",
+			Required:    mcpservers.ConfigRequiredTypeInitOptional,
+			Example:     defaultURL,
+			Description: "The Streamable http URL of the gezhe MCP server",
+		},
+		Type: model.ParamTypeURL,
+	},
+}

+ 30 - 0
mcp-servers/go.mod

@@ -15,34 +15,64 @@ require (
 )
 )
 
 
 require (
 require (
+	filippo.io/edwards25519 v1.1.0 // indirect
 	github.com/KyleBanks/depth v1.2.1 // indirect
 	github.com/KyleBanks/depth v1.2.1 // indirect
 	github.com/andybalholm/cascadia v1.3.3 // indirect
 	github.com/andybalholm/cascadia v1.3.3 // indirect
 	github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de // indirect
 	github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de // indirect
 	github.com/bytedance/sonic/loader v0.2.4 // indirect
 	github.com/bytedance/sonic/loader v0.2.4 // indirect
+	github.com/cespare/xxhash/v2 v2.3.0 // indirect
 	github.com/cloudwego/base64x v0.1.5 // indirect
 	github.com/cloudwego/base64x v0.1.5 // indirect
+	github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
+	github.com/dustin/go-humanize v1.0.1 // indirect
 	github.com/getkin/kin-openapi v0.132.0 // indirect
 	github.com/getkin/kin-openapi v0.132.0 // indirect
+	github.com/glebarez/go-sqlite v1.22.0 // indirect
+	github.com/glebarez/sqlite v1.11.0 // indirect
 	github.com/go-openapi/jsonpointer v0.21.1 // indirect
 	github.com/go-openapi/jsonpointer v0.21.1 // indirect
 	github.com/go-openapi/jsonreference v0.21.0 // indirect
 	github.com/go-openapi/jsonreference v0.21.0 // indirect
 	github.com/go-openapi/spec v0.21.0 // indirect
 	github.com/go-openapi/spec v0.21.0 // indirect
 	github.com/go-openapi/swag v0.23.1 // indirect
 	github.com/go-openapi/swag v0.23.1 // indirect
 	github.com/go-shiori/dom v0.0.0-20230515143342-73569d674e1c // indirect
 	github.com/go-shiori/dom v0.0.0-20230515143342-73569d674e1c // indirect
+	github.com/go-sql-driver/mysql v1.9.2 // indirect
 	github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f // indirect
 	github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f // indirect
 	github.com/google/uuid v1.6.0 // indirect
 	github.com/google/uuid v1.6.0 // indirect
+	github.com/jackc/pgpassfile v1.0.0 // indirect
+	github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
+	github.com/jackc/pgx/v5 v5.7.5 // indirect
+	github.com/jackc/puddle/v2 v2.2.2 // indirect
+	github.com/jinzhu/inflection v1.0.0 // indirect
+	github.com/jinzhu/now v1.1.5 // indirect
 	github.com/josharian/intern v1.0.0 // indirect
 	github.com/josharian/intern v1.0.0 // indirect
 	github.com/klauspost/cpuid/v2 v2.2.10 // indirect
 	github.com/klauspost/cpuid/v2 v2.2.10 // indirect
 	github.com/mailru/easyjson v0.9.0 // indirect
 	github.com/mailru/easyjson v0.9.0 // indirect
+	github.com/maruel/natural v1.1.1 // indirect
+	github.com/mattn/go-isatty v0.0.20 // indirect
 	github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
 	github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
+	github.com/ncruces/go-strftime v0.1.9 // indirect
 	github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037 // indirect
 	github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037 // indirect
 	github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90 // indirect
 	github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90 // indirect
 	github.com/perimeterx/marshmallow v1.1.5 // indirect
 	github.com/perimeterx/marshmallow v1.1.5 // indirect
+	github.com/redis/go-redis/v9 v9.8.0 // indirect
+	github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
+	github.com/shopspring/decimal v1.4.0 // indirect
+	github.com/sirupsen/logrus v1.9.3 // indirect
 	github.com/spf13/cast v1.8.0 // indirect
 	github.com/spf13/cast v1.8.0 // indirect
 	github.com/swaggo/swag v1.16.4 // indirect
 	github.com/swaggo/swag v1.16.4 // indirect
 	github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
 	github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
 	github.com/ugorji/go/codec v1.2.13 // indirect
 	github.com/ugorji/go/codec v1.2.13 // indirect
 	github.com/yosida95/uritemplate/v3 v3.0.2 // indirect
 	github.com/yosida95/uritemplate/v3 v3.0.2 // indirect
 	golang.org/x/arch v0.17.0 // indirect
 	golang.org/x/arch v0.17.0 // indirect
+	golang.org/x/crypto v0.38.0 // indirect
+	golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 // indirect
 	golang.org/x/net v0.40.0 // indirect
 	golang.org/x/net v0.40.0 // indirect
+	golang.org/x/sync v0.14.0 // indirect
 	golang.org/x/sys v0.33.0 // indirect
 	golang.org/x/sys v0.33.0 // indirect
 	golang.org/x/tools v0.33.0 // indirect
 	golang.org/x/tools v0.33.0 // indirect
 	gopkg.in/yaml.v3 v3.0.1 // indirect
 	gopkg.in/yaml.v3 v3.0.1 // indirect
+	gorm.io/driver/mysql v1.5.7 // indirect
+	gorm.io/driver/postgres v1.6.0 // indirect
+	gorm.io/gorm v1.30.0 // indirect
+	modernc.org/libc v1.65.8 // indirect
+	modernc.org/mathutil v1.7.1 // indirect
+	modernc.org/memory v1.11.0 // indirect
+	modernc.org/sqlite v1.37.1 // indirect
 )
 )

+ 40 - 0
mcp-servers/go.sum

@@ -1,3 +1,4 @@
+filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
 github.com/JohannesKaufmann/html-to-markdown v1.6.0 h1:04VXMiE50YYfCfLboJCLcgqF5x+rHJnb1ssNmqpLH/k=
 github.com/JohannesKaufmann/html-to-markdown v1.6.0 h1:04VXMiE50YYfCfLboJCLcgqF5x+rHJnb1ssNmqpLH/k=
 github.com/JohannesKaufmann/html-to-markdown v1.6.0/go.mod h1:NUI78lGg/a7vpEJTz/0uOcYMaibytE4BUOQS8k78yPQ=
 github.com/JohannesKaufmann/html-to-markdown v1.6.0/go.mod h1:NUI78lGg/a7vpEJTz/0uOcYMaibytE4BUOQS8k78yPQ=
 github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
 github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
@@ -9,21 +10,28 @@ github.com/andybalholm/cascadia v1.3.3 h1:AG2YHrzJIm4BZ19iwJ/DAua6Btl3IwJX+VI4kk
 github.com/andybalholm/cascadia v1.3.3/go.mod h1:xNd9bqTn98Ln4DwST8/nG+H0yuB8Hmgu1YHNnWw0GeA=
 github.com/andybalholm/cascadia v1.3.3/go.mod h1:xNd9bqTn98Ln4DwST8/nG+H0yuB8Hmgu1YHNnWw0GeA=
 github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de h1:FxWPpzIjnTlhPwqqXc4/vE0f7GvRjuAsbW+HOIe8KnA=
 github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de h1:FxWPpzIjnTlhPwqqXc4/vE0f7GvRjuAsbW+HOIe8KnA=
 github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de/go.mod h1:DCaWoUhZrYW9p1lxo/cm8EmUOOzAPSEZNGF2DK1dJgw=
 github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de/go.mod h1:DCaWoUhZrYW9p1lxo/cm8EmUOOzAPSEZNGF2DK1dJgw=
+github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
+github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
 github.com/bytedance/sonic v1.13.2 h1:8/H1FempDZqC4VqjptGo14QQlJx8VdZJegxs6wwfqpQ=
 github.com/bytedance/sonic v1.13.2 h1:8/H1FempDZqC4VqjptGo14QQlJx8VdZJegxs6wwfqpQ=
 github.com/bytedance/sonic v1.13.2/go.mod h1:o68xyaF9u2gvVBuGHPlUVCy+ZfmNNO5ETf1+KgkJhz4=
 github.com/bytedance/sonic v1.13.2/go.mod h1:o68xyaF9u2gvVBuGHPlUVCy+ZfmNNO5ETf1+KgkJhz4=
 github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
 github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
 github.com/bytedance/sonic/loader v0.2.4 h1:ZWCw4stuXUsn1/+zQDqeE7JKP+QO47tz7QCNan80NzY=
 github.com/bytedance/sonic/loader v0.2.4 h1:ZWCw4stuXUsn1/+zQDqeE7JKP+QO47tz7QCNan80NzY=
 github.com/bytedance/sonic/loader v0.2.4/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI=
 github.com/bytedance/sonic/loader v0.2.4/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI=
+github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
 github.com/cloudwego/base64x v0.1.5 h1:XPciSp1xaq2VCSt6lF0phncD4koWyULpl5bUxbfCyP4=
 github.com/cloudwego/base64x v0.1.5 h1:XPciSp1xaq2VCSt6lF0phncD4koWyULpl5bUxbfCyP4=
 github.com/cloudwego/base64x v0.1.5/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
 github.com/cloudwego/base64x v0.1.5/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
 github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
 github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
+github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
 github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
 github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
 github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
 github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
 github.com/getkin/kin-openapi v0.132.0 h1:3ISeLMsQzcb5v26yeJrBcdTCEQTag36ZjaGk7MIRUwk=
 github.com/getkin/kin-openapi v0.132.0 h1:3ISeLMsQzcb5v26yeJrBcdTCEQTag36ZjaGk7MIRUwk=
 github.com/getkin/kin-openapi v0.132.0/go.mod h1:3OlG51PCYNsPByuiMB0t4fjnNlIDnaEDsjiKUV8nL58=
 github.com/getkin/kin-openapi v0.132.0/go.mod h1:3OlG51PCYNsPByuiMB0t4fjnNlIDnaEDsjiKUV8nL58=
+github.com/glebarez/go-sqlite v1.22.0 h1:uAcMJhaA6r3LHMTFgP0SifzgXg46yJkgxqyuyec+ruQ=
+github.com/glebarez/sqlite v1.11.0 h1:wSG0irqzP6VurnMEpFGer5Li19RpIRi2qvQz++w0GMw=
 github.com/go-openapi/jsonpointer v0.21.1 h1:whnzv/pNXtK2FbX/W9yJfRmE2gsmkfahjMKB0fZvcic=
 github.com/go-openapi/jsonpointer v0.21.1 h1:whnzv/pNXtK2FbX/W9yJfRmE2gsmkfahjMKB0fZvcic=
 github.com/go-openapi/jsonpointer v0.21.1/go.mod h1:50I1STOfbY1ycR8jGz8DaMeLCdXiI6aDteEdRNNzpdk=
 github.com/go-openapi/jsonpointer v0.21.1/go.mod h1:50I1STOfbY1ycR8jGz8DaMeLCdXiI6aDteEdRNNzpdk=
 github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ=
 github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ=
@@ -36,6 +44,7 @@ github.com/go-shiori/dom v0.0.0-20230515143342-73569d674e1c h1:wpkoddUomPfHiOziH
 github.com/go-shiori/dom v0.0.0-20230515143342-73569d674e1c/go.mod h1:oVDCh3qjJMLVUSILBRwrm+Bc6RNXGZYtoh9xdvf1ffM=
 github.com/go-shiori/dom v0.0.0-20230515143342-73569d674e1c/go.mod h1:oVDCh3qjJMLVUSILBRwrm+Bc6RNXGZYtoh9xdvf1ffM=
 github.com/go-shiori/go-readability v0.0.0-20250217085726-9f5bf5ca7612 h1:BYLNYdZaepitbZreRIa9xeCQZocWmy/wj4cGIH0qyw0=
 github.com/go-shiori/go-readability v0.0.0-20250217085726-9f5bf5ca7612 h1:BYLNYdZaepitbZreRIa9xeCQZocWmy/wj4cGIH0qyw0=
 github.com/go-shiori/go-readability v0.0.0-20250217085726-9f5bf5ca7612/go.mod h1:wgqthQa8SAYs0yyljVeCOQlZ027VW5CmLsbi9jWC08c=
 github.com/go-shiori/go-readability v0.0.0-20250217085726-9f5bf5ca7612/go.mod h1:wgqthQa8SAYs0yyljVeCOQlZ027VW5CmLsbi9jWC08c=
+github.com/go-sql-driver/mysql v1.9.2 h1:4cNKDYQ1I84SXslGddlsrMhc8k4LeDVj6Ad6WRjiHuU=
 github.com/go-test/deep v1.1.1 h1:0r/53hagsehfO4bzD2Pgr/+RgHqhmf+k1Bpse2cTu1U=
 github.com/go-test/deep v1.1.1 h1:0r/53hagsehfO4bzD2Pgr/+RgHqhmf+k1Bpse2cTu1U=
 github.com/go-test/deep v1.1.1/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
 github.com/go-test/deep v1.1.1/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
 github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f h1:3BSP1Tbs2djlpprl7wCLuiqMaUh5SJkkzI2gDs+FgLs=
 github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f h1:3BSP1Tbs2djlpprl7wCLuiqMaUh5SJkkzI2gDs+FgLs=
@@ -43,8 +52,15 @@ github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f/go.mod h1:Pcatq5tYkCW
 github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
 github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
 github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
 github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
 github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
 github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
+github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e h1:ijClszYn+mADRFY17kjQEVQ1XRhq2/JR1M3sGqeJoxs=
 github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
 github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
 github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
+github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=
+github.com/jackc/pgx/v5 v5.7.5 h1:JHGfMnQY+IEtGM63d+NGMjoRpysB2JBwDr5fsngwmJs=
+github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo=
+github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
+github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
 github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
 github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
 github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
 github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
 github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
 github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
@@ -66,9 +82,12 @@ github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4
 github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=
 github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=
 github.com/mark3labs/mcp-go v0.30.0 h1:Taz7fiefkxY/l8jz1nA90V+WdM2eoMtlvwfWforVYbo=
 github.com/mark3labs/mcp-go v0.30.0 h1:Taz7fiefkxY/l8jz1nA90V+WdM2eoMtlvwfWforVYbo=
 github.com/mark3labs/mcp-go v0.30.0/go.mod h1:rXqOudj/djTORU/ThxYx8fqEVj/5pvTuuebQ2RC7uk4=
 github.com/mark3labs/mcp-go v0.30.0/go.mod h1:rXqOudj/djTORU/ThxYx8fqEVj/5pvTuuebQ2RC7uk4=
+github.com/maruel/natural v1.1.1 h1:Hja7XhhmvEFhcByqDoHz9QZbkWey+COd9xWfCfn1ioo=
+github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
 github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
 github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
 github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw=
 github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw=
 github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
 github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
+github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4=
 github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037 h1:G7ERwszslrBzRxj//JalHPu/3yz+De2J+4aLtSRlHiY=
 github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037 h1:G7ERwszslrBzRxj//JalHPu/3yz+De2J+4aLtSRlHiY=
 github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037/go.mod h1:2bpvgLBZEtENV5scfDFEtB/5+1M4hkQhDQrccEJ/qGw=
 github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037/go.mod h1:2bpvgLBZEtENV5scfDFEtB/5+1M4hkQhDQrccEJ/qGw=
 github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90 h1:bQx3WeLcUWy+RletIKwUIt4x3t8n2SxavmoclizMb8c=
 github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90 h1:bQx3WeLcUWy+RletIKwUIt4x3t8n2SxavmoclizMb8c=
@@ -78,6 +97,8 @@ github.com/perimeterx/marshmallow v1.1.5/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0V
 github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/redis/go-redis/v9 v9.8.0 h1:q3nRvjrlge/6UD7eTu/DSg2uYiU2mCL0G/uzBWqhicI=
+github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
 github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
 github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
 github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
 github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
 github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
 github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
@@ -87,6 +108,8 @@ github.com/sebdah/goldie/v2 v2.5.3/go.mod h1:oZ9fp0+se1eapSRjfYbsV/0Hqhbuu3bJVvK
 github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
 github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
 github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8=
 github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8=
 github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I=
 github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I=
+github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k=
+github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
 github.com/spf13/cast v1.8.0 h1:gEN9K4b8Xws4EX0+a0reLmhq8moKn7ntRlQYgjPeCDk=
 github.com/spf13/cast v1.8.0 h1:gEN9K4b8Xws4EX0+a0reLmhq8moKn7ntRlQYgjPeCDk=
 github.com/spf13/cast v1.8.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
 github.com/spf13/cast v1.8.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
@@ -122,6 +145,8 @@ golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDf
 golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
 golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
 golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
 golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
 golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
 golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
+golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8=
+golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 h1:y5zboxd6LQAqYIhHnB48p0ByQ/GnQx2BE33L8BOHQkI=
 golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
 golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
 golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
 golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
 golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
 golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
@@ -207,4 +232,19 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
 gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
 gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gorm.io/driver/mysql v1.5.7 h1:MndhOPYOfEp2rHKgkZIhJ16eVUIRf2HmzgoPmh7FCWo=
+gorm.io/driver/postgres v1.6.0 h1:2dxzU8xJ+ivvqTRph34QX+WrRaJlmfyPqXmoGVjMBa4=
+gorm.io/gorm v1.30.0 h1:qbT5aPv1UH8gI99OsRlvDToLxW5zR7FzS9acZDOZcgs=
+modernc.org/cc/v4 v4.26.1 h1:+X5NtzVBn0KgsBCBe+xkDC7twLb/jNVj9FPgiwSQO3s=
+modernc.org/ccgo/v4 v4.28.0 h1:rjznn6WWehKq7dG4JtLRKxb52Ecv8OUGah8+Z/SfpNU=
+modernc.org/fileutil v1.3.1 h1:8vq5fe7jdtEvoCf3Zf9Nm0Q05sH6kGx0Op2CPx1wTC8=
+modernc.org/gc/v2 v2.6.5 h1:nyqdV8q46KvTpZlsw66kWqwXRHdjIlJOhG6kxiV/9xI=
+modernc.org/libc v1.65.8 h1:7PXRJai0TXZ8uNA3srsmYzmTyrLoHImV5QxHeni108Q=
+modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU=
+modernc.org/memory v1.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI=
+modernc.org/opt v0.1.4 h1:2kNGMRiUjrp4LcaPuLY2PzUfqM/w9N23quVwhKt5Qm8=
+modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w=
+modernc.org/sqlite v1.37.1 h1:EgHJK/FPoqC+q2YBXg7fUmES37pCHFc97sI7zSayBEs=
+modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0=
+modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
 nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
 nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=

+ 2 - 1
mcp-servers/gpt-vis/init.go

@@ -3,6 +3,7 @@ package gptvis
 import (
 import (
 	_ "embed"
 	_ "embed"
 
 
+	"github.com/labring/aiproxy/core/model"
 	mcpservers "github.com/labring/aiproxy/mcp-servers"
 	mcpservers "github.com/labring/aiproxy/mcp-servers"
 )
 )
 
 
@@ -17,7 +18,7 @@ func init() {
 		mcpservers.NewMcp(
 		mcpservers.NewMcp(
 			"gpt-vis",
 			"gpt-vis",
 			"GPT Vis",
 			"GPT Vis",
-			mcpservers.McpTypeEmbed,
+			model.PublicMCPTypeEmbed,
 			mcpservers.WithNewServerFunc(NewServer),
 			mcpservers.WithNewServerFunc(NewServer),
 			mcpservers.WithGitHubURL(
 			mcpservers.WithGitHubURL(
 				"https://github.com/antvis/mcp-server-chart",
 				"https://github.com/antvis/mcp-server-chart",

+ 2 - 1
mcp-servers/hefeng-weather/init.go

@@ -3,6 +3,7 @@ package hefengweather
 import (
 import (
 	_ "embed"
 	_ "embed"
 
 
+	"github.com/labring/aiproxy/core/model"
 	mcpservers "github.com/labring/aiproxy/mcp-servers"
 	mcpservers "github.com/labring/aiproxy/mcp-servers"
 )
 )
 
 
@@ -17,7 +18,7 @@ func init() {
 		mcpservers.NewMcp(
 		mcpservers.NewMcp(
 			"hefeng-weather",
 			"hefeng-weather",
 			"HeFeng Weather",
 			"HeFeng Weather",
-			mcpservers.McpTypeEmbed,
+			model.PublicMCPTypeEmbed,
 			mcpservers.WithNewServerFunc(NewServer),
 			mcpservers.WithNewServerFunc(NewServer),
 			mcpservers.WithGitHubURL(
 			mcpservers.WithGitHubURL(
 				"https://github.com/shanggqm/hefeng-mcp-weather",
 				"https://github.com/shanggqm/hefeng-mcp-weather",

+ 2 - 1
mcp-servers/howtocook/init.go

@@ -3,6 +3,7 @@ package howtocook
 import (
 import (
 	_ "embed"
 	_ "embed"
 
 
+	"github.com/labring/aiproxy/core/model"
 	mcpservers "github.com/labring/aiproxy/mcp-servers"
 	mcpservers "github.com/labring/aiproxy/mcp-servers"
 )
 )
 
 
@@ -17,7 +18,7 @@ func init() {
 		mcpservers.NewMcp(
 		mcpservers.NewMcp(
 			"howtocook",
 			"howtocook",
 			"HowToCook Recipe Server",
 			"HowToCook Recipe Server",
-			mcpservers.McpTypeEmbed,
+			model.PublicMCPTypeEmbed,
 			mcpservers.WithNewServerFunc(NewServer),
 			mcpservers.WithNewServerFunc(NewServer),
 			mcpservers.WithDescription(
 			mcpservers.WithDescription(
 				"A recipe recommendation server based on the HowToCook project. Provides intelligent meal planning, recipe search by category, and dish recommendations based on the number of people.",
 				"A recipe recommendation server based on the HowToCook project. Provides intelligent meal planning, recipe search by category, and dish recommendations based on the number of people.",

+ 2 - 1
mcp-servers/jina-tools/init.go

@@ -3,6 +3,7 @@ package jinatools
 import (
 import (
 	_ "embed"
 	_ "embed"
 
 
+	"github.com/labring/aiproxy/core/model"
 	mcpservers "github.com/labring/aiproxy/mcp-servers"
 	mcpservers "github.com/labring/aiproxy/mcp-servers"
 )
 )
 
 
@@ -17,7 +18,7 @@ func init() {
 		mcpservers.NewMcp(
 		mcpservers.NewMcp(
 			"jina",
 			"jina",
 			"Jina AI Tools",
 			"Jina AI Tools",
-			mcpservers.McpTypeEmbed,
+			model.PublicMCPTypeEmbed,
 			mcpservers.WithNewServerFunc(NewServer),
 			mcpservers.WithNewServerFunc(NewServer),
 			mcpservers.WithGitHubURL(
 			mcpservers.WithGitHubURL(
 				"https://github.com/PsychArch/jina-mcp-tools",
 				"https://github.com/PsychArch/jina-mcp-tools",

+ 67 - 38
mcp-servers/mcp.go

@@ -3,6 +3,8 @@ package mcpservers
 import (
 import (
 	"errors"
 	"errors"
 	"fmt"
 	"fmt"
+
+	"github.com/labring/aiproxy/core/model"
 )
 )
 
 
 type ConfigValueValidator func(value string) error
 type ConfigValueValidator func(value string) error
@@ -50,6 +52,14 @@ type ConfigTemplate struct {
 
 
 type ConfigTemplates = map[string]ConfigTemplate
 type ConfigTemplates = map[string]ConfigTemplate
 
 
+type ProxyConfigTemplate struct {
+	ConfigTemplate
+	Type    model.ProxyParamType
+	Default string
+}
+
+type ProxyConfigTemplates = map[string]ProxyConfigTemplate
+
 func ValidateConfigTemplatesConfig(
 func ValidateConfigTemplatesConfig(
 	ct ConfigTemplates,
 	ct ConfigTemplates,
 	config, reusingConfig map[string]string,
 	config, reusingConfig map[string]string,
@@ -82,46 +92,48 @@ func ValidateConfigTemplatesConfig(
 
 
 func CheckConfigTemplatesValidate(ct ConfigTemplates) error {
 func CheckConfigTemplatesValidate(ct ConfigTemplates) error {
 	for key, value := range ct {
 	for key, value := range ct {
-		if value.Name == "" {
-			return fmt.Errorf("config %s name is required", key)
-		}
-		if value.Description == "" {
-			return fmt.Errorf("config %s description is required", key)
-		}
-		if value.Example == "" || value.Validator == nil {
-			continue
-		}
-		if err := value.Validator(value.Example); err != nil {
-			return fmt.Errorf("config %s example is invalid: %w", key, err)
+		err := CheckConfigTemplateValidate(value)
+		if err != nil {
+			return fmt.Errorf("config %s validate error: %w", key, err)
 		}
 		}
 	}
 	}
 	return nil
 	return nil
 }
 }
 
 
-type NewServerFunc func(config, reusingConfig map[string]string) (Server, error)
+func CheckProxyConfigTemplatesValidate(ct ProxyConfigTemplates) error {
+	for key, value := range ct {
+		err := CheckConfigTemplateValidate(value.ConfigTemplate)
+		if err != nil {
+			return fmt.Errorf("config %s validate error: %w", key, err)
+		}
+	}
+	return nil
+}
 
 
-type McpType string
+func CheckConfigTemplateValidate(value ConfigTemplate) error {
+	if value.Name == "" {
+		return errors.New("name is required")
+	}
+	if value.Description == "" {
+		return errors.New("description is required")
+	}
+	if value.Example == "" || value.Validator == nil {
+		return nil
+	}
+	if err := value.Validator(value.Example); err != nil {
+		return fmt.Errorf("example is invalid: %w", err)
+	}
+	return nil
+}
 
 
-const (
-	McpTypeEmbed McpType = "embed"
-	McpTypeDocs  McpType = "docs"
-)
+type NewServerFunc func(config, reusingConfig map[string]string) (Server, error)
 
 
 type McpServer struct {
 type McpServer struct {
-	ID              string
-	Name            string
-	Type            McpType
-	GitHubURL       string
-	Description     string
-	DescriptionCN   string
-	Readme          string
-	ReadmeURL       string
-	ReadmeCN        string
-	ReadmeCNURL     string
-	LogoURL         string
-	Tags            []string
-	ConfigTemplates ConfigTemplates
-	newServer       NewServerFunc
+	model.PublicMCP
+	ConfigTemplates      ConfigTemplates
+	ProxyConfigTemplates ProxyConfigTemplates
+	newServer            NewServerFunc
+	disableCache         bool
 }
 }
 
 
 type McpConfig func(*McpServer)
 type McpConfig func(*McpServer)
@@ -162,13 +174,13 @@ func WithReadmeCNURL(readmeCNURL string) McpConfig {
 	}
 	}
 }
 }
 
 
-func WithGitHubURL(githubURL string) McpConfig {
+func WithGitHubURL(gitHubURL string) McpConfig {
 	return func(e *McpServer) {
 	return func(e *McpServer) {
-		e.GitHubURL = githubURL
+		e.GitHubURL = gitHubURL
 	}
 	}
 }
 }
 
 
-func WithType(t McpType) McpConfig {
+func WithType(t model.PublicMCPType) McpConfig {
 	return func(e *McpServer) {
 	return func(e *McpServer) {
 		e.Type = t
 		e.Type = t
 	}
 	}
@@ -192,17 +204,31 @@ func WithConfigTemplates(configTemplates ConfigTemplates) McpConfig {
 	}
 	}
 }
 }
 
 
+func WithProxyConfigType(proxyConfigTemplates ProxyConfigTemplates) McpConfig {
+	return func(e *McpServer) {
+		e.ProxyConfigTemplates = proxyConfigTemplates
+	}
+}
+
 func WithNewServerFunc(newServer NewServerFunc) McpConfig {
 func WithNewServerFunc(newServer NewServerFunc) McpConfig {
 	return func(e *McpServer) {
 	return func(e *McpServer) {
 		e.newServer = newServer
 		e.newServer = newServer
 	}
 	}
 }
 }
 
 
-func NewMcp(id, name string, mcpType McpType, opts ...McpConfig) McpServer {
+func WithDisableCache(disableCache bool) McpConfig {
+	return func(e *McpServer) {
+		e.disableCache = disableCache
+	}
+}
+
+func NewMcp(id, name string, mcpType model.PublicMCPType, opts ...McpConfig) McpServer {
 	e := McpServer{
 	e := McpServer{
-		ID:   id,
-		Name: name,
-		Type: mcpType,
+		PublicMCP: model.PublicMCP{
+			ID:   id,
+			Name: name,
+			Type: mcpType,
+		},
 	}
 	}
 	for _, opt := range opts {
 	for _, opt := range opts {
 		opt(&e)
 		opt(&e)
@@ -211,6 +237,9 @@ func NewMcp(id, name string, mcpType McpType, opts ...McpConfig) McpServer {
 }
 }
 
 
 func (e *McpServer) NewServer(config, reusingConfig map[string]string) (Server, error) {
 func (e *McpServer) NewServer(config, reusingConfig map[string]string) (Server, error) {
+	if e.newServer != nil {
+		return nil, errors.New("not impl new server")
+	}
 	if err := ValidateConfigTemplatesConfig(e.ConfigTemplates, config, reusingConfig); err != nil {
 	if err := ValidateConfigTemplatesConfig(e.ConfigTemplates, config, reusingConfig); err != nil {
 		return nil, fmt.Errorf("mcp %s config is invalid: %w", e.ID, err)
 		return nil, fmt.Errorf("mcp %s config is invalid: %w", e.ID, err)
 	}
 	}

+ 3 - 0
mcp-servers/mcpregister/init.go

@@ -11,9 +11,12 @@ import (
 	_ "github.com/labring/aiproxy/mcp-servers/fetch"
 	_ "github.com/labring/aiproxy/mcp-servers/fetch"
 	_ "github.com/labring/aiproxy/mcp-servers/firecrawl"
 	_ "github.com/labring/aiproxy/mcp-servers/firecrawl"
 	_ "github.com/labring/aiproxy/mcp-servers/flomo"
 	_ "github.com/labring/aiproxy/mcp-servers/flomo"
+	_ "github.com/labring/aiproxy/mcp-servers/gezhe"
 	_ "github.com/labring/aiproxy/mcp-servers/gpt-vis"
 	_ "github.com/labring/aiproxy/mcp-servers/gpt-vis"
 	_ "github.com/labring/aiproxy/mcp-servers/hefeng-weather"
 	_ "github.com/labring/aiproxy/mcp-servers/hefeng-weather"
+	_ "github.com/labring/aiproxy/mcp-servers/howtocook"
 	_ "github.com/labring/aiproxy/mcp-servers/jina-tools"
 	_ "github.com/labring/aiproxy/mcp-servers/jina-tools"
+	_ "github.com/labring/aiproxy/mcp-servers/tavily"
 	_ "github.com/labring/aiproxy/mcp-servers/time"
 	_ "github.com/labring/aiproxy/mcp-servers/time"
 	_ "github.com/labring/aiproxy/mcp-servers/web-search"
 	_ "github.com/labring/aiproxy/mcp-servers/web-search"
 )
 )

+ 34 - 13
mcp-servers/register.go

@@ -7,6 +7,8 @@ import (
 	"sync"
 	"sync"
 	"sync/atomic"
 	"sync/atomic"
 	"time"
 	"time"
+
+	"github.com/labring/aiproxy/core/model"
 )
 )
 
 
 type mcpServerCacheItem struct {
 type mcpServerCacheItem struct {
@@ -57,32 +59,43 @@ func Register(mcp McpServer) {
 	if mcp.Name == "" {
 	if mcp.Name == "" {
 		panic("mcp name is required")
 		panic("mcp name is required")
 	}
 	}
-	if mcp.Description == "" && mcp.DescriptionCN == "" {
-		panic(fmt.Sprintf("mcp %s description or description_cn is required", mcp.ID))
+	if mcp.Description == "" &&
+		mcp.DescriptionCN == "" &&
+		mcp.Readme == "" &&
+		mcp.ReadmeURL == "" &&
+		mcp.ReadmeCN == "" &&
+		mcp.ReadmeCNURL == "" {
+		panic(
+			fmt.Sprintf(
+				"mcp %s description or description_cn readme or readme_url or readme_cn or readme_cn_url is required",
+				mcp.ID,
+			),
+		)
 	}
 	}
 	switch mcp.Type {
 	switch mcp.Type {
-	case McpTypeEmbed:
+	case model.PublicMCPTypeEmbed:
 		if mcp.newServer == nil {
 		if mcp.newServer == nil {
 			panic(fmt.Sprintf("mcp %s new server is required", mcp.ID))
 			panic(fmt.Sprintf("mcp %s new server is required", mcp.ID))
 		}
 		}
-	case McpTypeDocs:
-		if mcp.Readme == "" && mcp.ReadmeURL == "" && mcp.ReadmeCN == "" && mcp.ReadmeCNURL == "" {
-			panic(
-				fmt.Sprintf(
-					"mcp %s readme or readme_url or readme_cn or readme_cn_url is required",
-					mcp.ID,
-				),
-			)
+	case model.PublicMCPTypeProxySSE,
+		model.PublicMCPTypeProxyStreamable:
+		if len(mcp.ProxyConfigTemplates) == 0 {
+			panic(fmt.Sprintf("mcp %s proxy config templates is required", mcp.ID))
 		}
 		}
 	default:
 	default:
-		panic(fmt.Sprintf("mcp %s type is invalid", mcp.ID))
 	}
 	}
 
 
-	if mcp.ConfigTemplates != nil {
+	if len(mcp.ConfigTemplates) != 0 {
 		if err := CheckConfigTemplatesValidate(mcp.ConfigTemplates); err != nil {
 		if err := CheckConfigTemplatesValidate(mcp.ConfigTemplates); err != nil {
 			panic(fmt.Sprintf("mcp %s config templates example is invalid: %v", mcp.ID, err))
 			panic(fmt.Sprintf("mcp %s config templates example is invalid: %v", mcp.ID, err))
 		}
 		}
 	}
 	}
+	if len(mcp.ProxyConfigTemplates) != 0 {
+		if err := CheckProxyConfigTemplatesValidate(mcp.ProxyConfigTemplates); err != nil {
+			panic(fmt.Sprintf("mcp %s config templates example is invalid: %v", mcp.ID, err))
+		}
+	}
+
 	if _, ok := servers[mcp.ID]; ok {
 	if _, ok := servers[mcp.ID]; ok {
 		panic(fmt.Sprintf("mcp %s already registered", mcp.ID))
 		panic(fmt.Sprintf("mcp %s already registered", mcp.ID))
 	}
 	}
@@ -102,6 +115,14 @@ func GetMCPServer(id string, config, reusingConfig map[string]string) (Server, e
 		return nil, fmt.Errorf("mcp %s config is invalid: %w", id, err)
 		return nil, fmt.Errorf("mcp %s config is invalid: %w", id, err)
 	}
 	}
 
 
+	if embedServer.disableCache {
+		return embedServer.NewServer(config, reusingConfig)
+	}
+
+	if len(reusingConfig) == 0 {
+		return loadCacheServer(embedServer, config)
+	}
+
 	for _, template := range embedServer.ConfigTemplates {
 	for _, template := range embedServer.ConfigTemplates {
 		switch template.Required {
 		switch template.Required {
 		case ConfigRequiredTypeReusingOptional,
 		case ConfigRequiredTypeReusingOptional,

+ 2 - 1
mcp-servers/tavily/init.go

@@ -3,6 +3,7 @@ package tavily
 import (
 import (
 	_ "embed"
 	_ "embed"
 
 
+	"github.com/labring/aiproxy/core/model"
 	mcpservers "github.com/labring/aiproxy/mcp-servers"
 	mcpservers "github.com/labring/aiproxy/mcp-servers"
 )
 )
 
 
@@ -17,7 +18,7 @@ func init() {
 		mcpservers.NewMcp(
 		mcpservers.NewMcp(
 			"tavily",
 			"tavily",
 			"Tavily AI Search",
 			"Tavily AI Search",
-			mcpservers.McpTypeEmbed,
+			model.PublicMCPTypeEmbed,
 			mcpservers.WithNewServerFunc(NewServer),
 			mcpservers.WithNewServerFunc(NewServer),
 			mcpservers.WithDescription(
 			mcpservers.WithDescription(
 				"A powerful web search MCP server powered by Tavily's AI search engine. Provides real-time web search, content extraction, web crawling, and site mapping capabilities with advanced filtering and customization options.",
 				"A powerful web search MCP server powered by Tavily's AI search engine. Provides real-time web search, content extraction, web crawling, and site mapping capabilities with advanced filtering and customization options.",

+ 2 - 1
mcp-servers/time/init.go

@@ -3,6 +3,7 @@ package time
 import (
 import (
 	_ "embed"
 	_ "embed"
 
 
+	"github.com/labring/aiproxy/core/model"
 	mcpservers "github.com/labring/aiproxy/mcp-servers"
 	mcpservers "github.com/labring/aiproxy/mcp-servers"
 )
 )
 
 
@@ -17,7 +18,7 @@ func init() {
 		mcpservers.NewMcp(
 		mcpservers.NewMcp(
 			"time",
 			"time",
 			"Time",
 			"Time",
-			mcpservers.McpTypeEmbed,
+			model.PublicMCPTypeEmbed,
 			mcpservers.WithNewServerFunc(NewServer),
 			mcpservers.WithNewServerFunc(NewServer),
 			mcpservers.WithGitHubURL(
 			mcpservers.WithGitHubURL(
 				"https://github.com/modelcontextprotocol/servers/tree/main/src/time",
 				"https://github.com/modelcontextprotocol/servers/tree/main/src/time",

+ 2 - 1
mcp-servers/web-search/init.go

@@ -3,6 +3,7 @@ package websearch
 import (
 import (
 	_ "embed"
 	_ "embed"
 
 
+	"github.com/labring/aiproxy/core/model"
 	mcpservers "github.com/labring/aiproxy/mcp-servers"
 	mcpservers "github.com/labring/aiproxy/mcp-servers"
 )
 )
 
 
@@ -18,7 +19,7 @@ func init() {
 		mcpservers.NewMcp(
 		mcpservers.NewMcp(
 			"web-search",
 			"web-search",
 			"Web Search",
 			"Web Search",
-			mcpservers.McpTypeEmbed,
+			model.PublicMCPTypeEmbed,
 			mcpservers.WithNewServerFunc(NewServer),
 			mcpservers.WithNewServerFunc(NewServer),
 			mcpservers.WithConfigTemplates(configTemplates),
 			mcpservers.WithConfigTemplates(configTemplates),
 			mcpservers.WithTags([]string{"search", "web", "google", "bing", "arxiv", "searchxng"}),
 			mcpservers.WithTags([]string{"search", "web", "google", "bing", "arxiv", "searchxng"}),