mirror of
https://github.com/APIParkLab/APIPark.git
synced 2026-06-04 10:13:53 +08:00
MCP transports supports Streamable HTTP
This commit is contained in:
+71
-31
@@ -21,13 +21,14 @@ import (
|
||||
var _ IMcpController = (*imlMcpController)(nil)
|
||||
|
||||
type imlMcpController struct {
|
||||
settingModule system.ISettingModule `autowired:""`
|
||||
authorizationModule application_authorization.IAuthorizationModule `autowired:""`
|
||||
appModule service.IAppModule `autowired:""`
|
||||
mcpModule mcp.IMcpModule `autowired:""`
|
||||
sessionKeys sync.Map
|
||||
server map[string]http.Handler
|
||||
openServer http.Handler
|
||||
settingModule system.ISettingModule `autowired:""`
|
||||
authorizationModule application_authorization.IAuthorizationModule `autowired:""`
|
||||
appModule service.IAppModule `autowired:""`
|
||||
mcpModule mcp.IMcpModule `autowired:""`
|
||||
sessionKeys sync.Map
|
||||
sseServers map[string]http.Handler
|
||||
openSseServer http.Handler
|
||||
openStreamableServer http.Handler
|
||||
}
|
||||
|
||||
func (i *imlMcpController) AppMCPHandle(ctx *gin.Context) {
|
||||
@@ -42,12 +43,12 @@ func (i *imlMcpController) AppMCPHandle(ctx *gin.Context) {
|
||||
paths := strings.Split(req.URL.Path, "/")
|
||||
req.URL.Path = fmt.Sprintf("/api/v1/%s/%s", mcp_server.GlobalBasePath, paths[len(paths)-1])
|
||||
locale := utils.I18n(ctx)
|
||||
if v, ok := i.server[locale]; ok {
|
||||
if v, ok := i.sseServers[locale]; ok {
|
||||
v.ServeHTTP(ctx.Writer, req)
|
||||
return
|
||||
}
|
||||
|
||||
i.server[languageEnUs].ServeHTTP(ctx.Writer, req)
|
||||
i.sseServers[languageEnUs].ServeHTTP(ctx.Writer, req)
|
||||
}
|
||||
|
||||
func (i *imlMcpController) AppHandleSSE(ctx *gin.Context) {
|
||||
@@ -68,7 +69,7 @@ func (i *imlMcpController) AppHandleSSE(ctx *gin.Context) {
|
||||
}
|
||||
|
||||
ctx.Request.URL.Path = fmt.Sprintf("/openapi/v1/%s/sse", mcp_server.GlobalBasePath)
|
||||
i.handleSSE(ctx, i.openServer, SessionInfo{
|
||||
i.handleSSE(ctx, i.openSseServer, SessionInfo{
|
||||
Apikey: apikey,
|
||||
App: appId,
|
||||
})
|
||||
@@ -81,8 +82,29 @@ func (i *imlMcpController) AppHandleMessage(ctx *gin.Context) {
|
||||
return
|
||||
}
|
||||
ctx.Request.URL.Path = fmt.Sprintf("/openapi/v1/%s/message", mcp_server.GlobalBasePath)
|
||||
ctx.Request = ctx.Request.WithContext(utils.SetLabel(ctx.Request.Context(), "app", appId))
|
||||
i.handleMessage(ctx, i.openServer)
|
||||
//ctx.Request = ctx.Request.WithContext(utils.SetLabel(ctx.Request.Context(), "app", appId))
|
||||
i.handleMessage(ctx, i.openSseServer)
|
||||
}
|
||||
|
||||
func (i *imlMcpController) AppHandleStreamHTTP(ctx *gin.Context) {
|
||||
apikey := ctx.Request.Header.Get("Authorization")
|
||||
apikey = strings.TrimPrefix(apikey, "Bearer ")
|
||||
if apikey == "" {
|
||||
ctx.AbortWithStatusJSON(403, gin.H{"code": -1, "msg": "invalid apikey", "success": "fail"})
|
||||
return
|
||||
}
|
||||
appId := ctx.Request.Header.Get("X-Application-Id")
|
||||
if appId == "" {
|
||||
ctx.AbortWithStatusJSON(403, gin.H{"code": -1, "msg": "invalid app id", "success": "fail"})
|
||||
return
|
||||
}
|
||||
cfg := i.settingModule.Get(ctx)
|
||||
req := ctx.Request.WithContext(utils.SetGatewayInvoke(ctx.Request.Context(), cfg.InvokeAddress))
|
||||
|
||||
req = req.WithContext(utils.SetLabel(req.Context(), "apikey", apikey))
|
||||
req = req.WithContext(utils.SetLabel(req.Context(), "app", appId))
|
||||
req.URL.Path = mcp_server.OpenGlobalMCPPath
|
||||
i.openStreamableServer.ServeHTTP(ctx.Writer, req)
|
||||
}
|
||||
|
||||
func (i *imlMcpController) AppMCPConfig(ctx *gin.Context, appId string) (string, error) {
|
||||
@@ -94,36 +116,44 @@ func (i *imlMcpController) AppMCPConfig(ctx *gin.Context, appId string) (string,
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("get app info error: %v", err)
|
||||
}
|
||||
return fmt.Sprintf(mcpDefaultConfig, appInfo.Name, fmt.Sprintf("%s/openapi/v1/mcp/app/%s/sse?apikey={your_api_key}", strings.TrimSuffix(cfg.SitePrefix, "/"), appId)), nil
|
||||
}
|
||||
|
||||
var mcpDefaultConfig = `{
|
||||
"mcpServers": {
|
||||
"%s": {
|
||||
"url": "%s"
|
||||
}
|
||||
}
|
||||
return mcp_server.NewMCPConfig(
|
||||
mcp_server.TransportTypeStreamableHTTP,
|
||||
fmt.Sprintf("%s%s", strings.TrimSuffix(cfg.SitePrefix, "/"), mcp_server.OpenAppMCPPath),
|
||||
map[string]string{
|
||||
"Authorization": "Bearer {your_api_key}",
|
||||
"X-Application-Id": appId,
|
||||
},
|
||||
nil,
|
||||
).ToString(appInfo.Name), nil
|
||||
}
|
||||
`
|
||||
|
||||
func (i *imlMcpController) GlobalMCPConfig(ctx *gin.Context) (string, error) {
|
||||
cfg := i.settingModule.Get(ctx)
|
||||
if cfg.SitePrefix == "" {
|
||||
return "", fmt.Errorf("site prefix is empty")
|
||||
}
|
||||
return fmt.Sprintf(mcpDefaultConfig, "APIPark-MCP-Server", fmt.Sprintf("%s/openapi/v1/%s/sse?apikey={your_api_key}", strings.TrimSuffix(cfg.SitePrefix, "/"), mcp_server.GlobalBasePath)), nil
|
||||
return mcp_server.NewMCPConfig(
|
||||
mcp_server.TransportTypeStreamableHTTP,
|
||||
fmt.Sprintf("%s%s", strings.TrimSuffix(cfg.SitePrefix, "/"), mcp_server.OpenGlobalMCPPath),
|
||||
map[string]string{
|
||||
"Authorization": "Bearer {your_api_key}",
|
||||
},
|
||||
nil,
|
||||
).ToString("APIPark-MCP-Server"), nil
|
||||
}
|
||||
|
||||
func (i *imlMcpController) OnComplete() {
|
||||
i.server = make(map[string]http.Handler)
|
||||
i.sseServers = make(map[string]http.Handler)
|
||||
for language, tools := range mcpToolsByLanguage {
|
||||
s := server.NewMCPServer("APIPark MCP Server", "1.0.0", server.WithLogging())
|
||||
s.AddTool(tools[ToolServiceList], i.mcpModule.Services)
|
||||
s.AddTool(tools[ToolOpenAPIDocument], i.mcpModule.APIs)
|
||||
s.AddTool(tools[ToolInvokeAPI], i.mcpModule.Invoke)
|
||||
i.server[language] = server.NewSSEServer(s, server.WithStaticBasePath(fmt.Sprintf("/api/v1/%s", mcp_server.GlobalBasePath)))
|
||||
i.sseServers[language] = server.NewSSEServer(s, server.WithStaticBasePath(fmt.Sprintf("/api/v1/%s", mcp_server.GlobalBasePath)))
|
||||
if language == languageEnUs {
|
||||
i.openServer = server.NewSSEServer(s, server.WithStaticBasePath(fmt.Sprintf("/openapi/v1/%s", strings.Trim(mcp_server.GlobalBasePath, "/"))))
|
||||
i.openSseServer = server.NewSSEServer(s, server.WithStaticBasePath(fmt.Sprintf("/openapi/v1/%s", strings.Trim(mcp_server.GlobalBasePath, "/"))))
|
||||
i.openStreamableServer = server.NewStreamableHTTPServer(s, server.WithEndpointPath(mcp_server.OpenGlobalMCPPath))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -132,16 +162,16 @@ func (i *imlMcpController) GlobalMCPHandle(ctx *gin.Context) {
|
||||
cfg := i.settingModule.Get(ctx)
|
||||
req := ctx.Request.WithContext(utils.SetGatewayInvoke(ctx.Request.Context(), cfg.InvokeAddress))
|
||||
locale := utils.I18n(ctx)
|
||||
if v, ok := i.server[locale]; ok {
|
||||
if v, ok := i.sseServers[locale]; ok {
|
||||
v.ServeHTTP(ctx.Writer, req)
|
||||
return
|
||||
}
|
||||
i.server[languageEnUs].ServeHTTP(ctx.Writer, req)
|
||||
i.sseServers[languageEnUs].ServeHTTP(ctx.Writer, req)
|
||||
}
|
||||
|
||||
func (i *imlMcpController) GlobalHandleSSE(ctx *gin.Context) {
|
||||
apikey := ctx.Request.URL.Query().Get("apikey")
|
||||
i.handleSSE(ctx, i.openServer, SessionInfo{
|
||||
i.handleSSE(ctx, i.openSseServer, SessionInfo{
|
||||
Apikey: apikey,
|
||||
})
|
||||
}
|
||||
@@ -167,7 +197,16 @@ func (i *imlMcpController) handleSSE(ctx *gin.Context, server http.Handler, sIn
|
||||
}
|
||||
|
||||
func (i *imlMcpController) GlobalHandleMessage(ctx *gin.Context) {
|
||||
i.handleMessage(ctx, i.openServer)
|
||||
i.handleMessage(ctx, i.openSseServer)
|
||||
}
|
||||
|
||||
func (i *imlMcpController) GlobalHandleStreamHTTP(ctx *gin.Context) {
|
||||
apikey := ctx.Request.Header.Get("Authorization")
|
||||
apikey = strings.TrimPrefix(apikey, "Bearer ")
|
||||
cfg := i.settingModule.Get(ctx)
|
||||
req := ctx.Request.WithContext(utils.SetGatewayInvoke(ctx.Request.Context(), cfg.InvokeAddress))
|
||||
req = req.WithContext(utils.SetLabel(req.Context(), "apikey", apikey))
|
||||
i.openStreamableServer.ServeHTTP(ctx.Writer, req)
|
||||
}
|
||||
|
||||
func (i *imlMcpController) MCPHandle(ctx *gin.Context) {
|
||||
@@ -204,12 +243,13 @@ func (i *imlMcpController) ServiceHandleMessage(ctx *gin.Context) {
|
||||
}
|
||||
|
||||
func (i *imlMcpController) ServiceHandleStreamHTTP(ctx *gin.Context) {
|
||||
apikey := ctx.Request.URL.Query().Get("apikey")
|
||||
serviceId := ctx.Param("serviceId")
|
||||
apikey := ctx.Request.Header.Get("Authorization")
|
||||
serviceId := ctx.Request.Header.Get("X-Service-Id")
|
||||
if serviceId == "" {
|
||||
ctx.AbortWithStatusJSON(403, gin.H{"code": -1, "msg": "invalid service id", "success": "fail"})
|
||||
return
|
||||
}
|
||||
apikey = strings.TrimPrefix(apikey, "Bearer ")
|
||||
ok, err := i.authorizationModule.CheckAPIKeyAuthorizationByService(ctx, serviceId, apikey)
|
||||
if err != nil {
|
||||
ctx.AbortWithStatusJSON(403, gin.H{"code": -1, "msg": err.Error(), "success": "fail"})
|
||||
|
||||
@@ -13,11 +13,13 @@ type IMcpController interface {
|
||||
GlobalMCPHandle(ctx *gin.Context)
|
||||
GlobalHandleSSE(ctx *gin.Context)
|
||||
GlobalHandleMessage(ctx *gin.Context)
|
||||
GlobalHandleStreamHTTP(ctx *gin.Context)
|
||||
GlobalMCPConfig(ctx *gin.Context) (string, error)
|
||||
|
||||
AppMCPHandle(ctx *gin.Context)
|
||||
AppHandleSSE(ctx *gin.Context)
|
||||
AppHandleMessage(ctx *gin.Context)
|
||||
AppHandleStreamHTTP(ctx *gin.Context)
|
||||
AppMCPConfig(ctx *gin.Context, appId string) (string, error)
|
||||
|
||||
ServiceHandleSSE(ctx *gin.Context)
|
||||
|
||||
@@ -15,7 +15,7 @@ require (
|
||||
github.com/go-sql-driver/mysql v1.7.0
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/influxdata/influxdb-client-go/v2 v2.14.0
|
||||
github.com/mark3labs/mcp-go v0.33.0
|
||||
github.com/mark3labs/mcp-go v0.42.0-beta.3
|
||||
github.com/mitchellh/mapstructure v1.5.0
|
||||
github.com/nsqio/go-nsq v1.1.0
|
||||
github.com/ollama/ollama v0.5.8
|
||||
@@ -27,6 +27,8 @@ require (
|
||||
|
||||
require (
|
||||
github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect
|
||||
github.com/bahlo/generic-list-go v0.2.0 // indirect
|
||||
github.com/buger/jsonparser v1.1.1 // indirect
|
||||
github.com/bytedance/sonic v1.11.6 // indirect
|
||||
github.com/bytedance/sonic/loader v0.1.1 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||
@@ -46,6 +48,7 @@ require (
|
||||
github.com/golang/snappy v0.0.4 // indirect
|
||||
github.com/gorilla/websocket v1.4.2 // indirect
|
||||
github.com/influxdata/line-protocol v0.0.0-20200327222509-2487e7298839 // indirect
|
||||
github.com/invopop/jsonschema v0.13.0 // 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
|
||||
@@ -67,6 +70,7 @@ require (
|
||||
github.com/spf13/cast v1.7.1 // indirect
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||
github.com/ugorji/go/codec v1.2.12 // indirect
|
||||
github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect
|
||||
github.com/yosida95/uritemplate/v3 v3.0.2 // indirect
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.13 // indirect
|
||||
go.uber.org/atomic v1.7.0 // indirect
|
||||
@@ -87,6 +91,4 @@ require (
|
||||
//)
|
||||
|
||||
//replace github.com/eolinker/ap-account => ../../eolinker/ap-account
|
||||
|
||||
//
|
||||
//replace github.com/eolinker/go-common => ../../eolinker/go-common
|
||||
|
||||
@@ -2,6 +2,8 @@ github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2
|
||||
github.com/RaveNoX/go-jsoncommentstrip v1.0.0/go.mod h1:78ihd09MekBnJnxpICcwzCMzGrKSKYe4AqU6PDYYpjk=
|
||||
github.com/apapsch/go-jsonmerge/v2 v2.0.0 h1:axGnT1gRIfimI7gJifB699GoE/oq+F2MU7Dml6nw9rQ=
|
||||
github.com/apapsch/go-jsonmerge/v2 v2.0.0/go.mod h1:lvDnEdqiQrp0O42VQGgmlKpxL1AP2+08jFMw88y4klk=
|
||||
github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk=
|
||||
github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg=
|
||||
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
|
||||
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
|
||||
github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w=
|
||||
@@ -9,6 +11,8 @@ github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
|
||||
github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
|
||||
github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
|
||||
github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
|
||||
github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs=
|
||||
github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0=
|
||||
github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0=
|
||||
github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4=
|
||||
github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM=
|
||||
@@ -80,6 +84,8 @@ github.com/influxdata/influxdb-client-go/v2 v2.14.0 h1:AjbBfJuq+QoaXNcrova8smSjw
|
||||
github.com/influxdata/influxdb-client-go/v2 v2.14.0/go.mod h1:Ahpm3QXKMJslpXl3IftVLVezreAUtBOTZssDrjZEFHI=
|
||||
github.com/influxdata/line-protocol v0.0.0-20200327222509-2487e7298839 h1:W9WBk7wlPfJLvMCdtV4zPulc4uCPrlywQOmbFOhgQNU=
|
||||
github.com/influxdata/line-protocol v0.0.0-20200327222509-2487e7298839/go.mod h1:xaLFMmpvUxqXtVkUJfg9QmT88cDaCJ3ZKgdZ78oO8Qo=
|
||||
github.com/invopop/jsonschema v0.13.0 h1:KvpoAJWEjR3uD9Kbm2HWJmqsEaHt8lBUpd0qHcIi21E=
|
||||
github.com/invopop/jsonschema v0.13.0/go.mod h1:ffZ5Km5SWWRAIN6wbDXItl95euhFz2uON45H2qjYt+0=
|
||||
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
||||
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
|
||||
@@ -101,8 +107,8 @@ github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
||||
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
||||
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
|
||||
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||
github.com/mark3labs/mcp-go v0.33.0 h1:naxhjnTIs/tyPZmWUZFuG0lDmdA6sUyYGGf3gsHvTCc=
|
||||
github.com/mark3labs/mcp-go v0.33.0/go.mod h1:rXqOudj/djTORU/ThxYx8fqEVj/5pvTuuebQ2RC7uk4=
|
||||
github.com/mark3labs/mcp-go v0.42.0-beta.3 h1:nmOg1HFgSOgy0bZkAQ+E6qVpPMPmE8hIkM0BO94Ks9k=
|
||||
github.com/mark3labs/mcp-go v0.42.0-beta.3/go.mod h1:YnJfOL382MIWDx1kMY+2zsRHU/q78dBg9aFb8W6Thdw=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||
@@ -159,6 +165,8 @@ github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65E
|
||||
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
||||
github.com/urfave/cli v1.22.16 h1:MH0k6uJxdwdeWQTwhSO42Pwr4YLrNLwBtg1MRgTqPdQ=
|
||||
github.com/urfave/cli v1.22.16/go.mod h1:EeJR6BKodywf4zciqrdw6hpCPk68JO9z5LazXZMn5Po=
|
||||
github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/fJgbpc=
|
||||
github.com/wk8/go-ordered-map/v2 v2.1.8/go.mod h1:5nJHM5DyteebpVlHnWMV0rPz6Zp7+xBAnxjb1X5vnTw=
|
||||
github.com/yosida95/uritemplate/v3 v3.0.2 h1:Ed3Oyj9yrmi9087+NczuL5BwkIc4wvTb5zIM+UJPGz4=
|
||||
github.com/yosida95/uritemplate/v3 v3.0.2/go.mod h1:ILOh0sOhIJR3+L/8afwt/kE++YT040gmv5BQTMR2HP4=
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.13 h1:RVZSAnWWWiI5IrYAXjQorajncORbS0zI48LQlE2kQWg=
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
package mcp_server
|
||||
|
||||
import "encoding/json"
|
||||
|
||||
type TransportType string
|
||||
|
||||
const (
|
||||
TransportTypeStreamableHTTP TransportType = "streamable-http"
|
||||
TransportTypeSSE TransportType = "sse"
|
||||
)
|
||||
|
||||
func NewMCPConfig(typ TransportType, url string, headers map[string]string, alwaysAllow []string) *MCPConfig {
|
||||
return &MCPConfig{
|
||||
Type: typ,
|
||||
URL: url,
|
||||
Headers: headers,
|
||||
AlwaysAllow: alwaysAllow,
|
||||
}
|
||||
}
|
||||
|
||||
type MCPConfig struct {
|
||||
Type TransportType `json:"type"`
|
||||
URL string `json:"url"`
|
||||
Headers map[string]string `json:"headers,omitempty"`
|
||||
AlwaysAllow []string `json:"alwaysAllow,omitempty"`
|
||||
}
|
||||
|
||||
func (c *MCPConfig) ToString(name string) string {
|
||||
m := map[string]interface{}{
|
||||
"mcpServers": map[string]interface{}{
|
||||
name: c,
|
||||
},
|
||||
}
|
||||
data, _ := json.MarshalIndent(m, "", "\t")
|
||||
return string(data)
|
||||
}
|
||||
+21
-6
@@ -17,6 +17,10 @@ var (
|
||||
ServiceBasePath = "mcp/service"
|
||||
GlobalBasePath = "mcp/global"
|
||||
AppBasePath = "mcp/app"
|
||||
|
||||
OpenGlobalMCPPath = "/openapi/v1/global/mcp"
|
||||
OpenAppMCPPath = "/openapi/v1/app/mcp"
|
||||
OpenServiceMCPPath = "/openapi/v1/service/mcp"
|
||||
)
|
||||
|
||||
func NewServer() *Server {
|
||||
@@ -61,7 +65,7 @@ func (s *Server) Set(id string, ser *server.MCPServer) {
|
||||
}
|
||||
tmp.handlers["api-sse"] = server.NewSSEServer(ser, server.WithStaticBasePath(fmt.Sprintf("/api/v1/%s/%s", ServiceBasePath, id)))
|
||||
tmp.handlers["openapi-sse"] = server.NewSSEServer(ser, server.WithStaticBasePath(fmt.Sprintf("/openapi/v1/%s/%s", ServiceBasePath, id)))
|
||||
tmp.handlers["openapi-stream"] = server.NewStreamableHTTPServer(ser, server.WithEndpointPath(fmt.Sprintf("/openapi/v1/%s/%s/mcp", ServiceBasePath, id)))
|
||||
tmp.handlers["openapi-stream"] = server.NewStreamableHTTPServer(ser, server.WithEndpointPath(OpenServiceMCPPath))
|
||||
s.servers[id] = tmp
|
||||
|
||||
}
|
||||
@@ -91,12 +95,23 @@ func (s *Server) Get(id string) (*Handler, bool) {
|
||||
}
|
||||
|
||||
func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
sid, err := genPath(r.URL.Path)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
w.Write([]byte(err.Error()))
|
||||
return
|
||||
var sid string
|
||||
if r.URL.Path == OpenServiceMCPPath {
|
||||
sid = r.Header.Get("X-Service-Id")
|
||||
if sid == "" {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
id, err := genPath(r.URL.Path)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
w.Write([]byte(err.Error()))
|
||||
return
|
||||
}
|
||||
sid = id
|
||||
}
|
||||
|
||||
ser, has := s.Get(sid)
|
||||
if has {
|
||||
ser.ServeHTTP(w, r)
|
||||
|
||||
+11
-13
@@ -9,10 +9,10 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
service_overview "github.com/APIParkLab/APIPark/service/service-overview"
|
||||
|
||||
mcp_server "github.com/APIParkLab/APIPark/mcp-server"
|
||||
|
||||
service_overview "github.com/APIParkLab/APIPark/service/service-overview"
|
||||
|
||||
"github.com/APIParkLab/APIPark/module/monitor/driver"
|
||||
|
||||
"github.com/APIParkLab/APIPark/service/monitor"
|
||||
@@ -326,15 +326,6 @@ func (i *imlCatalogueModule) Subscribe(ctx context.Context, subscribeInfo *catal
|
||||
})
|
||||
}
|
||||
|
||||
var mcpDefaultConfig = `{
|
||||
"mcpServers": {
|
||||
"%s": {
|
||||
"url": "%s"
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
func (i *imlCatalogueModule) ServiceDetail(ctx context.Context, sid string) (*catalogue_dto.ServiceDetail, error) {
|
||||
// 获取服务的基本信息
|
||||
s, err := i.serviceService.Get(ctx, sid)
|
||||
@@ -400,8 +391,15 @@ func (i *imlCatalogueModule) ServiceDetail(ctx context.Context, sid string) (*ca
|
||||
mcpAccessConfig := ""
|
||||
if s.EnableMCP {
|
||||
if sitePrefix != "" {
|
||||
mcpAccessAddress = fmt.Sprintf("%s/openapi/v1/%s/%s/sse?apikey={your_api_key}", strings.TrimSuffix(sitePrefix, "/"), mcp_server.ServiceBasePath, s.Id)
|
||||
mcpAccessConfig = fmt.Sprintf(mcpDefaultConfig, fmt.Sprintf("APIPark/%s", s.Name), mcpAccessAddress)
|
||||
mcpAccessConfig = mcp_server.NewMCPConfig(
|
||||
mcp_server.TransportTypeStreamableHTTP,
|
||||
fmt.Sprintf("%s/openapi/v1/service/mcp", strings.TrimSuffix(sitePrefix, "/")),
|
||||
map[string]string{
|
||||
"Authorization": "Bearer {your_api_key}",
|
||||
"X-Service-Id": s.Id,
|
||||
},
|
||||
nil,
|
||||
).ToString(fmt.Sprintf("APIPark/%s", s.Name))
|
||||
}
|
||||
}
|
||||
invokeMap, err := i.ProviderStatistics(ctx, time.Now().Add(-24*30*time.Hour), time.Now(), s.Id)
|
||||
|
||||
@@ -17,12 +17,16 @@ func (p *plugin) mcpAPIs() []pm3.Api {
|
||||
globalMessagePath := fmt.Sprintf("/api/v1/%s/message", mcp_server.GlobalBasePath)
|
||||
appSSEPath := fmt.Sprintf("/api/v1/%s/sse", mcp_server.AppBasePath)
|
||||
appMessagePath := fmt.Sprintf("/api/v1/%s/message", mcp_server.AppBasePath)
|
||||
globalMcpPath := fmt.Sprintf("/api/v1/%s/mcp", mcp_server.GlobalBasePath)
|
||||
appMcpPath := fmt.Sprintf("/api/v1/%s/mcp", mcp_server.AppBasePath)
|
||||
ignore.IgnorePath("login", http.MethodGet, serviceSSEPath)
|
||||
ignore.IgnorePath("login", http.MethodPost, serviceMessagePath)
|
||||
ignore.IgnorePath("login", http.MethodGet, globalSSEPath)
|
||||
ignore.IgnorePath("login", http.MethodPost, globalMessagePath)
|
||||
ignore.IgnorePath("login", http.MethodGet, appSSEPath)
|
||||
ignore.IgnorePath("login", http.MethodPost, appMessagePath)
|
||||
ignore.IgnorePath("login", http.MethodGet, globalMcpPath)
|
||||
ignore.IgnorePath("login", http.MethodGet, appMcpPath)
|
||||
return []pm3.Api{
|
||||
pm3.CreateApiSimple(http.MethodGet, serviceSSEPath, p.mcpController.MCPHandle),
|
||||
pm3.CreateApiSimple(http.MethodPost, serviceMessagePath, p.mcpController.MCPHandle),
|
||||
|
||||
@@ -60,6 +60,7 @@ func (o *openapiCheck) Handler(ginCtx *gin.Context) {
|
||||
}
|
||||
authorization = apikey
|
||||
}
|
||||
authorization = strings.TrimPrefix(authorization, "Bearer ")
|
||||
if authorization == o.apikey {
|
||||
return
|
||||
}
|
||||
|
||||
+21
-8
@@ -19,17 +19,22 @@ func (p *plugin) mcpAPIs() []pm3.Api {
|
||||
|
||||
serviceSSEPath := fmt.Sprintf("/openapi/v1/%s/:serviceId/sse", strings.Trim(mcp_server.ServiceBasePath, "/"))
|
||||
serviceMessagePath := fmt.Sprintf("/openapi/v1/%s/:serviceId/message", strings.Trim(mcp_server.ServiceBasePath, "/"))
|
||||
serviceStreamablePath := fmt.Sprintf("/openapi/v1/%s/:serviceId/mcp", strings.Trim(mcp_server.ServiceBasePath, "/"))
|
||||
//serviceStreamablePath := fmt.Sprintf("/openapi/v1/%s/:serviceId/mcp", strings.Trim(mcp_server.ServiceBasePath, "/"))
|
||||
|
||||
ignore.IgnorePath("openapi", http.MethodPost, globalMessagePath)
|
||||
ignore.IgnorePath("openapi", http.MethodGet, globalSSEPath)
|
||||
//ignore.IgnorePath("openapi", http.MethodGet, globalSSEPath)
|
||||
ignore.IgnorePath("openapi", http.MethodPost, appMessagePath)
|
||||
ignore.IgnorePath("openapi", http.MethodGet, appSSEPath)
|
||||
ignore.IgnorePath("openapi", http.MethodGet, serviceSSEPath)
|
||||
ignore.IgnorePath("openapi", http.MethodPost, serviceMessagePath)
|
||||
ignore.IgnorePath("openapi", http.MethodGet, serviceStreamablePath)
|
||||
ignore.IgnorePath("openapi", http.MethodPost, serviceStreamablePath)
|
||||
ignore.IgnorePath("openapi", http.MethodDelete, serviceStreamablePath)
|
||||
|
||||
ignore.IgnorePath("openapi", http.MethodGet, mcp_server.OpenAppMCPPath)
|
||||
ignore.IgnorePath("openapi", http.MethodPost, mcp_server.OpenAppMCPPath)
|
||||
ignore.IgnorePath("openapi", http.MethodDelete, mcp_server.OpenAppMCPPath)
|
||||
|
||||
ignore.IgnorePath("openapi", http.MethodGet, mcp_server.OpenServiceMCPPath)
|
||||
ignore.IgnorePath("openapi", http.MethodPost, mcp_server.OpenServiceMCPPath)
|
||||
ignore.IgnorePath("openapi", http.MethodDelete, mcp_server.OpenServiceMCPPath)
|
||||
|
||||
return []pm3.Api{
|
||||
pm3.CreateApiSimple(http.MethodGet, globalSSEPath, p.mcpController.GlobalHandleSSE),
|
||||
@@ -41,9 +46,17 @@ func (p *plugin) mcpAPIs() []pm3.Api {
|
||||
pm3.CreateApiSimple(http.MethodGet, serviceSSEPath, p.mcpController.ServiceHandleSSE),
|
||||
pm3.CreateApiSimple(http.MethodPost, serviceMessagePath, p.mcpController.ServiceHandleMessage),
|
||||
|
||||
pm3.CreateApiSimple(http.MethodPost, serviceStreamablePath, p.mcpController.ServiceHandleStreamHTTP),
|
||||
pm3.CreateApiSimple(http.MethodDelete, serviceStreamablePath, p.mcpController.ServiceHandleStreamHTTP),
|
||||
pm3.CreateApiSimple(http.MethodGet, serviceStreamablePath, p.mcpController.ServiceHandleStreamHTTP),
|
||||
pm3.CreateApiSimple(http.MethodGet, mcp_server.OpenGlobalMCPPath, p.mcpController.GlobalHandleStreamHTTP),
|
||||
pm3.CreateApiSimple(http.MethodPost, mcp_server.OpenGlobalMCPPath, p.mcpController.GlobalHandleStreamHTTP),
|
||||
pm3.CreateApiSimple(http.MethodDelete, mcp_server.OpenGlobalMCPPath, p.mcpController.GlobalHandleStreamHTTP),
|
||||
|
||||
pm3.CreateApiSimple(http.MethodGet, mcp_server.OpenAppMCPPath, p.mcpController.AppHandleStreamHTTP),
|
||||
pm3.CreateApiSimple(http.MethodPost, mcp_server.OpenAppMCPPath, p.mcpController.AppHandleStreamHTTP),
|
||||
pm3.CreateApiSimple(http.MethodDelete, mcp_server.OpenAppMCPPath, p.mcpController.AppHandleStreamHTTP),
|
||||
|
||||
pm3.CreateApiSimple(http.MethodPost, mcp_server.OpenServiceMCPPath, p.mcpController.ServiceHandleStreamHTTP),
|
||||
pm3.CreateApiSimple(http.MethodDelete, mcp_server.OpenServiceMCPPath, p.mcpController.ServiceHandleStreamHTTP),
|
||||
pm3.CreateApiSimple(http.MethodGet, mcp_server.OpenServiceMCPPath, p.mcpController.ServiceHandleStreamHTTP),
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -3,10 +3,11 @@ package commit
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"github.com/eolinker/go-common/utils"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/eolinker/go-common/utils"
|
||||
|
||||
"github.com/eolinker/go-common/store"
|
||||
"github.com/google/uuid"
|
||||
"gorm.io/gorm"
|
||||
|
||||
Reference in New Issue
Block a user