From cab42b3c7dea85dc5b33da5632153fb78a314185 Mon Sep 17 00:00:00 2001 From: CityFun <31820853+zhengkunwang223@users.noreply.github.com> Date: Wed, 30 Jul 2025 18:54:51 +0800 Subject: [PATCH] feat: Mcp Server support streamableHttp (#9761) Refs https://github.com/1Panel-dev/1Panel/issues/8482 --- agent/app/dto/request/mcp_server.go | 20 ++++++----- agent/app/model/mcp_server.go | 28 ++++++++------- agent/app/service/mcp_server.go | 34 ++++++++++++++----- agent/cmd/server/mcp/compose.yml | 2 ++ agent/init/migration/migrate.go | 1 + agent/init/migration/migrations/init.go | 13 +++++++ agent/utils/docker/docker.go | 12 +++++++ frontend/src/api/interface/ai.ts | 2 ++ frontend/src/lang/modules/en.ts | 3 ++ frontend/src/lang/modules/ja.ts | 3 ++ frontend/src/lang/modules/ko.ts | 3 ++ frontend/src/lang/modules/ms.ts | 3 ++ frontend/src/lang/modules/pt-br.ts | 3 ++ frontend/src/lang/modules/ru.ts | 3 ++ frontend/src/lang/modules/tr.ts | 3 ++ frontend/src/lang/modules/zh-Hant.ts | 3 ++ frontend/src/lang/modules/zh.ts | 3 ++ .../src/views/ai/mcp/server/operate/index.vue | 23 ++++++++++++- 18 files changed, 130 insertions(+), 32 deletions(-) diff --git a/agent/app/dto/request/mcp_server.go b/agent/app/dto/request/mcp_server.go index 984c287db..9bd5d088c 100644 --- a/agent/app/dto/request/mcp_server.go +++ b/agent/app/dto/request/mcp_server.go @@ -9,15 +9,17 @@ type McpServerSearch struct { } type McpServerCreate struct { - Name string `json:"name" validate:"required"` - Command string `json:"command" validate:"required"` - Environments []Environment `json:"environments"` - Volumes []Volume `json:"volumes"` - Port int `json:"port" validate:"required"` - ContainerName string `json:"containerName"` - BaseURL string `json:"baseUrl"` - SsePath string `json:"ssePath"` - HostIP string `json:"hostIP"` + Name string `json:"name" validate:"required"` + Command string `json:"command" validate:"required"` + Environments []Environment `json:"environments"` + Volumes []Volume `json:"volumes"` + Port int `json:"port" validate:"required"` + ContainerName string `json:"containerName"` + BaseURL string `json:"baseUrl"` + SsePath string `json:"ssePath"` + HostIP string `json:"hostIP"` + StreamableHttpPath string `json:"streamableHttpPath"` + OutputTransport string `json:"outputTransport" validate:"required"` } type McpServerUpdate struct { diff --git a/agent/app/model/mcp_server.go b/agent/app/model/mcp_server.go index 61ce66b65..e3207db8a 100644 --- a/agent/app/model/mcp_server.go +++ b/agent/app/model/mcp_server.go @@ -2,17 +2,19 @@ package model type McpServer struct { BaseModel - Name string `json:"name"` - DockerCompose string `json:"dockerCompose"` - Command string `json:"command"` - ContainerName string `json:"containerName"` - Message string `json:"message"` - Port int `json:"port"` - Status string `json:"status"` - Env string `json:"env"` - BaseURL string `json:"baseUrl"` - SsePath string `json:"ssePath"` - WebsiteID int `json:"websiteID"` - Dir string `json:"dir"` - HostIP string `json:"hostIP"` + Name string `json:"name"` + DockerCompose string `json:"dockerCompose"` + Command string `json:"command"` + ContainerName string `json:"containerName"` + Message string `json:"message"` + Port int `json:"port"` + Status string `json:"status"` + Env string `json:"env"` + BaseURL string `json:"baseUrl"` + SsePath string `json:"ssePath"` + WebsiteID int `json:"websiteID"` + Dir string `json:"dir"` + HostIP string `json:"hostIP"` + StreamableHttpPath string `json:"streamableHttpPath"` + OutputTransport string `json:"outputTransport"` } diff --git a/agent/app/service/mcp_server.go b/agent/app/service/mcp_server.go index 4b76fa1de..df6b9e023 100644 --- a/agent/app/service/mcp_server.go +++ b/agent/app/service/mcp_server.go @@ -86,6 +86,11 @@ func (m McpServerService) Page(req request.McpServerSearch) response.McpServersR } func (m McpServerService) Update(req request.McpServerUpdate) error { + go func() { + if err := docker.PullImage("supercorp/supergateway:latest"); err != nil { + global.LOG.Errorf("docker pull mcp image error: %s", err.Error()) + } + }() mcpServer, err := mcpServerRepo.GetFirst(repo.WithByID(req.ID)) if err != nil { return err @@ -108,6 +113,8 @@ func (m McpServerService) Update(req request.McpServerUpdate) error { mcpServer.BaseURL = req.BaseURL mcpServer.SsePath = req.SsePath mcpServer.HostIP = req.HostIP + mcpServer.StreamableHttpPath = req.StreamableHttpPath + mcpServer.OutputTransport = req.OutputTransport if err := handleCreateParams(mcpServer, req.Environments, req.Volumes); err != nil { return err } @@ -130,6 +137,11 @@ func (m McpServerService) Update(req request.McpServerUpdate) error { } func (m McpServerService) Create(create request.McpServerCreate) error { + go func() { + if err := docker.PullImage("supercorp/supergateway:latest"); err != nil { + global.LOG.Errorf("docker pull mcp image error: %s", err.Error()) + } + }() servers, _ := mcpServerRepo.List() for _, server := range servers { if server.Port == create.Port { @@ -154,15 +166,17 @@ func (m McpServerService) Create(create request.McpServerCreate) error { } mcpDir := path.Join(global.Dir.McpDir, create.Name) mcpServer := &model.McpServer{ - Name: create.Name, - ContainerName: create.ContainerName, - Port: create.Port, - Command: create.Command, - Status: constant.StatusStarting, - BaseURL: create.BaseURL, - SsePath: create.SsePath, - Dir: mcpDir, - HostIP: create.HostIP, + Name: create.Name, + ContainerName: create.ContainerName, + Port: create.Port, + Command: create.Command, + Status: constant.StatusStarting, + BaseURL: create.BaseURL, + SsePath: create.SsePath, + Dir: mcpDir, + HostIP: create.HostIP, + StreamableHttpPath: create.StreamableHttpPath, + OutputTransport: create.OutputTransport, } if err := handleCreateParams(mcpServer, create.Environments, create.Volumes); err != nil { return err @@ -524,6 +538,8 @@ func handleEnv(mcpServer *model.McpServer) gotenv.Env { env["BASE_URL"] = mcpServer.BaseURL env["SSE_PATH"] = mcpServer.SsePath env["HOST_IP"] = mcpServer.HostIP + env["STREAMABLE_HTTP_PATH"] = mcpServer.StreamableHttpPath + env["OUTPUT_TRANSPORT"] = mcpServer.OutputTransport envStr, _ := gotenv.Marshal(env) mcpServer.Env = envStr return env diff --git a/agent/cmd/server/mcp/compose.yml b/agent/cmd/server/mcp/compose.yml index 9aef87157..433ee2278 100644 --- a/agent/cmd/server/mcp/compose.yml +++ b/agent/cmd/server/mcp/compose.yml @@ -7,9 +7,11 @@ services: - "${HOST_IP}:${PANEL_APP_PORT_HTTP}:${PANEL_APP_PORT_HTTP}" command: [ "--stdio", "${COMMAND}", + "--outputTransport","${OUTPUT_TRANSPORT}", "--port", "${PANEL_APP_PORT_HTTP}", "--baseUrl", "${BASE_URL}", "--ssePath", "${SSE_PATH}", + "--streamableHttpPath", "${STREAMABLE_HTTP_PATH}", "--messagePath", "${SSE_PATH}/messages" ] networks: diff --git a/agent/init/migration/migrate.go b/agent/init/migration/migrate.go index fe27532af..4d5b1da20 100644 --- a/agent/init/migration/migrate.go +++ b/agent/init/migration/migrate.go @@ -33,6 +33,7 @@ func InitAgentDB() { migrations.InitAlertConfig, migrations.AddMethodToAlertLog, migrations.AddMethodToAlertTask, + migrations.UpdateMcpServer, }) if err := m.Migrate(); err != nil { global.LOG.Error(err) diff --git a/agent/init/migration/migrations/init.go b/agent/init/migration/migrations/init.go index af465a146..7dab00f6d 100644 --- a/agent/init/migration/migrations/init.go +++ b/agent/init/migration/migrations/init.go @@ -419,3 +419,16 @@ var AddMethodToAlertTask = &gormigrate.Migration{ return nil }, } + +var UpdateMcpServer = &gormigrate.Migration{ + ID: "20250729-update-mcp-server", + Migrate: func(tx *gorm.DB) error { + if err := tx.AutoMigrate(&model.McpServer{}); err != nil { + return err + } + if err := tx.Model(&model.McpServer{}).Where("1=1").Update("output_transport", "sse").Error; err != nil { + return err + } + return nil + }, +} diff --git a/agent/utils/docker/docker.go b/agent/utils/docker/docker.go index 6030f2adc..eaaff8dc8 100644 --- a/agent/utils/docker/docker.go +++ b/agent/utils/docker/docker.go @@ -353,3 +353,15 @@ func logProcess(progress map[string]interface{}, task *task.Task) { } _ = setLog(id, progressStr, task) } + +func PullImage(imageName string) error { + cli, err := NewDockerClient() + if err != nil { + return err + } + defer cli.Close() + if _, err := cli.ImagePull(context.Background(), imageName, image.PullOptions{}); err != nil { + return err + } + return nil +} diff --git a/frontend/src/api/interface/ai.ts b/frontend/src/api/interface/ai.ts index 037ad7a63..9e51bc982 100644 --- a/frontend/src/api/interface/ai.ts +++ b/frontend/src/api/interface/ai.ts @@ -137,6 +137,8 @@ export namespace AI { hostIP: string; protocol: string; url: string; + outputTransport: string; + streamableHttpPath: string; } export interface McpServerSearch extends ReqPage { diff --git a/frontend/src/lang/modules/en.ts b/frontend/src/lang/modules/en.ts index 5b35e6aa1..7461289a2 100644 --- a/frontend/src/lang/modules/en.ts +++ b/frontend/src/lang/modules/en.ts @@ -706,6 +706,9 @@ const message = { importMcpJsonError: 'mcpServers structure is incorrect', bindDomainHelper: 'After binding the website, it will modify the access address of all installed MCP Servers and close external access to the ports', + outputTransport: 'Output Type', + streamableHttpPath: 'Streaming Path', + streamableHttpPathHelper: 'For example: /mcp, note that it should not overlap with other Servers', }, }, container: { diff --git a/frontend/src/lang/modules/ja.ts b/frontend/src/lang/modules/ja.ts index ef6c02440..faee02fa3 100644 --- a/frontend/src/lang/modules/ja.ts +++ b/frontend/src/lang/modules/ja.ts @@ -692,6 +692,9 @@ const message = { importMcpJsonError: 'mcpServers 構造が正しくありません', bindDomainHelper: 'ウェブサイトをバインドした後、インストールされたすべての MCP サーバーのアクセスアドレスを変更し、ポートへの外部アクセスを閉じます', + outputTransport: '出力タイプ', + streamableHttpPath: 'ストリーミングパス', + streamableHttpPathHelper: '例:/mcp、他のサーバーと重複しないように注意してください', }, }, container: { diff --git a/frontend/src/lang/modules/ko.ts b/frontend/src/lang/modules/ko.ts index c15ddcfa6..8afce23ec 100644 --- a/frontend/src/lang/modules/ko.ts +++ b/frontend/src/lang/modules/ko.ts @@ -688,6 +688,9 @@ const message = { importMcpJsonError: 'mcpServers 構造が正しくありません', bindDomainHelper: '웹사이트를 바인딩한 후, 설치된 모든 MCP 서버의 접근 주소를 수정하고 포트의 외부 접근을 닫습니다', + outputTransport: '출력 유형', + streamableHttpPath: '스트리밍 경로', + streamableHttpPathHelper: '예: /mcp, 다른 서버와 중복되지 않도록 주의하세요', }, }, container: { diff --git a/frontend/src/lang/modules/ms.ts b/frontend/src/lang/modules/ms.ts index bf4a5ed17..035360f75 100644 --- a/frontend/src/lang/modules/ms.ts +++ b/frontend/src/lang/modules/ms.ts @@ -705,6 +705,9 @@ const message = { importMcpJsonError: 'Struktur mcpServers tidak betul', bindDomainHelper: 'Setelah mengikat laman web, ia akan mengubah alamat akses semua Pelayan MCP yang dipasang dan menutup akses luaran ke pelabuhan', + outputTransport: 'Jenis Output', + streamableHttpPath: 'Laluan Streaming', + streamableHttpPathHelper: 'Contoh: /mcp, elakkan daripada bertindan dengan pelayan lain', }, }, container: { diff --git a/frontend/src/lang/modules/pt-br.ts b/frontend/src/lang/modules/pt-br.ts index 01c782285..594ada2aa 100644 --- a/frontend/src/lang/modules/pt-br.ts +++ b/frontend/src/lang/modules/pt-br.ts @@ -701,6 +701,9 @@ const message = { importMcpJsonError: 'A estrutura mcpServers está incorreta', bindDomainHelper: 'Após vincular o site, ele modificará o endereço de acesso de todos os servidores MCP instalados e fechará o acesso externo às portas', + outputTransport: 'Tipo de Saída', + streamableHttpPath: 'Caminho de Streaming', + streamableHttpPathHelper: 'Por exemplo: /mcp, certifique-se de que não se sobreponha a outros Servidores', }, }, container: { diff --git a/frontend/src/lang/modules/ru.ts b/frontend/src/lang/modules/ru.ts index 853b4e29a..f2fc11171 100644 --- a/frontend/src/lang/modules/ru.ts +++ b/frontend/src/lang/modules/ru.ts @@ -698,6 +698,9 @@ const message = { importMcpJsonError: 'Структура mcpServers некорректна', bindDomainHelper: 'После привязки веб-сайта он изменит адрес доступа для всех установленных серверов MCP и закроет внешний доступ к портам', + outputTransport: 'Тип вывода', + streamableHttpPath: 'Путь потоковой передачи', + streamableHttpPathHelper: 'Например: /mcp, обратите внимание, чтобы не перекрывать другие серверы', }, }, container: { diff --git a/frontend/src/lang/modules/tr.ts b/frontend/src/lang/modules/tr.ts index 90e3df85a..2a753467c 100644 --- a/frontend/src/lang/modules/tr.ts +++ b/frontend/src/lang/modules/tr.ts @@ -716,6 +716,9 @@ const message = { importMcpJsonError: 'mcpServers yapısı yanlış', bindDomainHelper: 'Web sitesini bağladıktan sonra, kurulu tüm MCP Sunucularının erişim adresini değiştirecek ve portlara harici erişimi kapatacaktır', + outputTransport: 'Çıktı Türü', + streamableHttpPath: 'Akış Yolu', + streamableHttpPathHelper: 'Örneğin: /mcp, diğer Sunucularla çakışmaması gerektiğine dikkat edin', }, }, container: { diff --git a/frontend/src/lang/modules/zh-Hant.ts b/frontend/src/lang/modules/zh-Hant.ts index f34d6c50d..ea957af0e 100644 --- a/frontend/src/lang/modules/zh-Hant.ts +++ b/frontend/src/lang/modules/zh-Hant.ts @@ -680,6 +680,9 @@ const message = { importMcpJson: '導入 MCP Server配置', importMcpJsonError: 'mcpServers 結構不正確', bindDomainHelper: '綁定網站之後會修改所有已安裝 MCP Server 的訪問地址,並關閉端口的外部訪問', + outputTransport: '輸出類型', + streamableHttpPath: '流式傳輸路徑', + streamableHttpPathHelper: '例如:/mcp, 注意不要與其他 Server 重複', }, }, container: { diff --git a/frontend/src/lang/modules/zh.ts b/frontend/src/lang/modules/zh.ts index f2a963e75..fd4679296 100644 --- a/frontend/src/lang/modules/zh.ts +++ b/frontend/src/lang/modules/zh.ts @@ -679,6 +679,9 @@ const message = { importMcpJson: '导入 MCP Server 配置', importMcpJsonError: 'mcpServers 结构不正确', bindDomainHelper: '绑定网站之后会修改所有已安装 MCP Server 的访问地址,并关闭端口的外部访问', + outputTransport: '输出类型', + streamableHttpPath: '流式传输路径', + streamableHttpPathHelper: '例如:/mcp, 注意不要与其他 Server 重复', }, }, container: { diff --git a/frontend/src/views/ai/mcp/server/operate/index.vue b/frontend/src/views/ai/mcp/server/operate/index.vue index edb8dbc05..0c9d2037d 100644 --- a/frontend/src/views/ai/mcp/server/operate/index.vue +++ b/frontend/src/views/ai/mcp/server/operate/index.vue @@ -91,12 +91,28 @@ - + + + + + + + {{ $t('aiTools.mcp.ssePathHelper') }} + + + + {{ $t('aiTools.mcp.streamableHttpPathHelper') }} + +