From 23876ed1866abf47decea447c334555a07431fa1 Mon Sep 17 00:00:00 2001
From: CityFun <31820853+zhengkunwang223@users.noreply.github.com>
Date: Thu, 4 Sep 2025 15:21:33 +0800
Subject: [PATCH] feat: Mcp Server support uvx (#10261)
Refs https://github.com/1Panel-dev/1Panel/issues/10118
---
agent/app/dto/request/mcp_server.go | 1 +
agent/app/model/mcp_server.go | 1 +
agent/app/service/mcp_server.go | 39 +++++++++++++------
agent/init/migration/migrate.go | 1 +
agent/init/migration/migrations/init.go | 13 +++++++
frontend/src/api/interface/ai.ts | 1 +
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 | 22 +++++++++--
16 files changed, 80 insertions(+), 25 deletions(-)
diff --git a/agent/app/dto/request/mcp_server.go b/agent/app/dto/request/mcp_server.go
index 9bd5d088c..721cfb00e 100644
--- a/agent/app/dto/request/mcp_server.go
+++ b/agent/app/dto/request/mcp_server.go
@@ -20,6 +20,7 @@ type McpServerCreate struct {
HostIP string `json:"hostIP"`
StreamableHttpPath string `json:"streamableHttpPath"`
OutputTransport string `json:"outputTransport" validate:"required"`
+ Type string `json:"type" validate:"required"`
}
type McpServerUpdate struct {
diff --git a/agent/app/model/mcp_server.go b/agent/app/model/mcp_server.go
index e3207db8a..133bc8f44 100644
--- a/agent/app/model/mcp_server.go
+++ b/agent/app/model/mcp_server.go
@@ -17,4 +17,5 @@ type McpServer struct {
HostIP string `json:"hostIP"`
StreamableHttpPath string `json:"streamableHttpPath"`
OutputTransport string `json:"outputTransport"`
+ Type string `json:"type"`
}
diff --git a/agent/app/service/mcp_server.go b/agent/app/service/mcp_server.go
index df6b9e023..ddc0c1257 100644
--- a/agent/app/service/mcp_server.go
+++ b/agent/app/service/mcp_server.go
@@ -59,7 +59,11 @@ func (m McpServerService) Page(req request.McpServerSearch) response.McpServersR
Environments: make([]request.Environment, 0),
Volumes: make([]request.Volume, 0),
}
- project, _ := docker.GetComposeProject(item.Name, path.Join(global.Dir.McpDir, item.Name), []byte(item.DockerCompose), []byte(item.Env), true)
+ project, err := docker.GetComposeProject(item.Name, path.Join(global.Dir.McpDir, item.Name), []byte(item.DockerCompose), []byte(item.Env), true)
+ if err != nil {
+ global.LOG.Errorf("get mcp compose project error: %s", err.Error())
+ continue
+ }
for _, service := range project.Services {
if service.Environment != nil {
for key, value := range service.Environment {
@@ -86,11 +90,7 @@ 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())
- }
- }()
+ go pullImage(req.Type)
mcpServer, err := mcpServerRepo.GetFirst(repo.WithByID(req.ID))
if err != nil {
return err
@@ -115,6 +115,7 @@ func (m McpServerService) Update(req request.McpServerUpdate) error {
mcpServer.HostIP = req.HostIP
mcpServer.StreamableHttpPath = req.StreamableHttpPath
mcpServer.OutputTransport = req.OutputTransport
+ mcpServer.Type = req.Type
if err := handleCreateParams(mcpServer, req.Environments, req.Volumes); err != nil {
return err
}
@@ -137,11 +138,7 @@ 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())
- }
- }()
+ go pullImage(create.Type)
servers, _ := mcpServerRepo.List()
for _, server := range servers {
if server.Port == create.Port {
@@ -177,6 +174,7 @@ func (m McpServerService) Create(create request.McpServerCreate) error {
HostIP: create.HostIP,
StreamableHttpPath: create.StreamableHttpPath,
OutputTransport: create.OutputTransport,
+ Type: create.Type,
}
if err := handleCreateParams(mcpServer, create.Environments, create.Volumes); err != nil {
return err
@@ -590,6 +588,11 @@ func handleCreateParams(mcpServer *model.McpServer, environments []request.Envir
}
serviceValue["volumes"] = volumeList
}
+ if mcpServer.Type == "npx" {
+ serviceValue["image"] = "supercorp/supergateway:latest"
+ } else {
+ serviceValue["image"] = "supercorp/supergateway:uvx"
+ }
services[mcpServer.Name] = serviceValue
composeByte, err := yaml.Marshal(composeMap)
@@ -641,7 +644,7 @@ func syncMcpServerContainerStatus(mcpServer *model.McpServer) error {
case "restarting":
mcpServer.Status = constant.StatusRestarting
default:
- if mcpServer.Status != constant.StatusBuilding {
+ if mcpServer.Status != constant.StatusStarting {
mcpServer.Status = constant.StatusStopped
}
}
@@ -656,3 +659,15 @@ func GetWebsiteID() uint {
websiteIDUint, _ := strconv.ParseUint(websiteID.Value, 10, 64)
return uint(websiteIDUint)
}
+
+func pullImage(imageType string) {
+ if imageType == "npx" {
+ if err := docker.PullImage("supercorp/supergateway:latest"); err != nil {
+ global.LOG.Errorf("docker pull mcp image error: %s", err.Error())
+ }
+ } else {
+ if err := docker.PullImage("supercorp/supergateway:uvx"); err != nil {
+ global.LOG.Errorf("docker pull mcp image error: %s", err.Error())
+ }
+ }
+}
diff --git a/agent/init/migration/migrate.go b/agent/init/migration/migrate.go
index 6a7ed525d..80c5274ce 100644
--- a/agent/init/migration/migrate.go
+++ b/agent/init/migration/migrate.go
@@ -38,6 +38,7 @@ func InitAgentDB() {
migrations.AddColumnToAlert,
migrations.UpdateWebsiteSSL,
migrations.AddQuickJump,
+ migrations.UpdateMcpServerAddType,
})
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 c3a7f6271..4efade8ca 100644
--- a/agent/init/migration/migrations/init.go
+++ b/agent/init/migration/migrations/init.go
@@ -496,3 +496,16 @@ var AddQuickJump = &gormigrate.Migration{
return nil
},
}
+
+var UpdateMcpServerAddType = &gormigrate.Migration{
+ ID: "20250904-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("type", "npx").Error; err != nil {
+ return err
+ }
+ return nil
+ },
+}
diff --git a/frontend/src/api/interface/ai.ts b/frontend/src/api/interface/ai.ts
index 9e51bc982..4ff02dae9 100644
--- a/frontend/src/api/interface/ai.ts
+++ b/frontend/src/api/interface/ai.ts
@@ -139,6 +139,7 @@ export namespace AI {
url: string;
outputTransport: string;
streamableHttpPath: string;
+ type: string;
}
export interface McpServerSearch extends ReqPage {
diff --git a/frontend/src/lang/modules/en.ts b/frontend/src/lang/modules/en.ts
index e84d50682..6a2196f41 100644
--- a/frontend/src/lang/modules/en.ts
+++ b/frontend/src/lang/modules/en.ts
@@ -696,7 +696,6 @@ const message = {
server: 'MCP Server',
create: 'Add MCP Server',
edit: 'Edit MCP Server',
- commandHelper: 'For example: npx -y {0}',
baseUrl: 'External Access Path',
baseUrlHelper: 'For example: http://192.168.1.2:8000',
ssePath: 'SSE Path',
@@ -717,6 +716,8 @@ const message = {
outputTransport: 'Output Type',
streamableHttpPath: 'Streaming Path',
streamableHttpPathHelper: 'For example: /mcp, note that it should not overlap with other Servers',
+ npxHelper: 'Suitable for mcp started with npx or binary',
+ uvxHelper: 'Suitable for mcp started with uvx',
},
},
container: {
diff --git a/frontend/src/lang/modules/ja.ts b/frontend/src/lang/modules/ja.ts
index c361aff66..1865510f1 100644
--- a/frontend/src/lang/modules/ja.ts
+++ b/frontend/src/lang/modules/ja.ts
@@ -684,7 +684,6 @@ const message = {
server: 'MCP サーバー',
create: 'サーバーを追加',
edit: 'サーバーを編集',
- commandHelper: '例: npx -y {0}',
baseUrl: '外部アクセスパス',
baseUrlHelper: '例: http://192.168.1.2:8000',
ssePath: 'SSE パス',
@@ -705,6 +704,8 @@ const message = {
outputTransport: '出力タイプ',
streamableHttpPath: 'ストリーミングパス',
streamableHttpPathHelper: '例:/mcp、他のサーバーと重複しないように注意してください',
+ npxHelper: 'npx またはバイナリで起動する mcp に適しています',
+ uvxHelper: 'uvx で起動する mcp に適しています',
},
},
container: {
diff --git a/frontend/src/lang/modules/ko.ts b/frontend/src/lang/modules/ko.ts
index 0a94f0f72..82e623601 100644
--- a/frontend/src/lang/modules/ko.ts
+++ b/frontend/src/lang/modules/ko.ts
@@ -680,7 +680,6 @@ const message = {
server: 'MCP サーバー',
create: 'サーバーを追加',
edit: 'サーバーを編集',
- commandHelper: '例: npx -y {0}',
baseUrl: '外部アクセスパス',
baseUrlHelper: '例: http://192.168.1.2:8000',
ssePath: 'SSE パス',
@@ -701,6 +700,8 @@ const message = {
outputTransport: '출력 유형',
streamableHttpPath: '스트리밍 경로',
streamableHttpPathHelper: '예: /mcp, 다른 서버와 중복되지 않도록 주의하세요',
+ npxHelper: 'npx 또는 바이너리로 시작하는 mcp에 적합',
+ uvxHelper: 'uvx로 시작하는 mcp에 적합',
},
},
container: {
diff --git a/frontend/src/lang/modules/ms.ts b/frontend/src/lang/modules/ms.ts
index e42b6b202..f8b97ad0f 100644
--- a/frontend/src/lang/modules/ms.ts
+++ b/frontend/src/lang/modules/ms.ts
@@ -697,7 +697,6 @@ const message = {
server: 'Pelayan MCP',
create: 'Tambah Pelayan',
edit: 'Edit Pelayan',
- commandHelper: 'Contoh: npx -y {0}',
baseUrl: 'Laluan Akses Luar',
baseUrlHelper: 'Contoh: http://192.168.1.2:8000',
ssePath: 'Laluan SSE',
@@ -718,6 +717,8 @@ const message = {
outputTransport: 'Jenis Output',
streamableHttpPath: 'Laluan Streaming',
streamableHttpPathHelper: 'Contoh: /mcp, elakkan daripada bertindan dengan pelayan lain',
+ npxHelper: 'Sesuai untuk mcp yang dimulakan dengan npx atau binari',
+ uvxHelper: 'Sesuai untuk mcp yang dimulakan dengan uvx',
},
},
container: {
diff --git a/frontend/src/lang/modules/pt-br.ts b/frontend/src/lang/modules/pt-br.ts
index e91efb45e..b2555831c 100644
--- a/frontend/src/lang/modules/pt-br.ts
+++ b/frontend/src/lang/modules/pt-br.ts
@@ -693,7 +693,6 @@ const message = {
server: 'Servidor MCP',
create: 'Adicionar Servidor',
edit: 'Editar Servidor',
- commandHelper: 'Por exemplo: npx -y {0}',
baseUrl: 'Caminho de Acesso Externo',
baseUrlHelper: 'Por exemplo: http://192.168.1.2:8000',
ssePath: 'Caminho SSE',
@@ -714,6 +713,8 @@ const message = {
outputTransport: 'Tipo de Saída',
streamableHttpPath: 'Caminho de Streaming',
streamableHttpPathHelper: 'Por exemplo: /mcp, certifique-se de que não se sobreponha a outros Servidores',
+ npxHelper: 'Adequado para mcp iniciado com npx ou binário',
+ uvxHelper: 'Adequado para mcp iniciado com uvx',
},
},
container: {
diff --git a/frontend/src/lang/modules/ru.ts b/frontend/src/lang/modules/ru.ts
index a0a909043..32f3832c0 100644
--- a/frontend/src/lang/modules/ru.ts
+++ b/frontend/src/lang/modules/ru.ts
@@ -690,7 +690,6 @@ const message = {
server: 'Сервер MCP',
create: 'Добавить сервер',
edit: 'Редактировать сервер',
- commandHelper: 'Например: npx -y {0}',
baseUrl: 'Внешний путь доступа',
baseUrlHelper: 'Например: http://192.168.1.2:8000',
ssePath: 'Путь SSE',
@@ -711,6 +710,8 @@ const message = {
outputTransport: 'Тип вывода',
streamableHttpPath: 'Путь потоковой передачи',
streamableHttpPathHelper: 'Например: /mcp, обратите внимание, чтобы не перекрывать другие серверы',
+ npxHelper: 'Подходит для mcp, запущенного с помощью npx или бинарного файла',
+ uvxHelper: 'Подходит для mcp, запущенного с помощью uvx',
},
},
container: {
diff --git a/frontend/src/lang/modules/tr.ts b/frontend/src/lang/modules/tr.ts
index 5d0221c5a..8492f3b2f 100644
--- a/frontend/src/lang/modules/tr.ts
+++ b/frontend/src/lang/modules/tr.ts
@@ -706,7 +706,6 @@ const message = {
server: 'MCP Sunucusu',
create: 'MCP Sunucusu Ekle',
edit: 'MCP Sunucusunu Düzenle',
- commandHelper: 'Örneğin: npx -y {0}',
baseUrl: 'Harici Erişim Yolu',
baseUrlHelper: 'Örneğin: http://192.168.1.2:8000',
ssePath: 'SSE Yolu',
@@ -727,6 +726,8 @@ const message = {
outputTransport: 'Çıktı Türü',
streamableHttpPath: 'Akış Yolu',
streamableHttpPathHelper: 'Örneğin: /mcp, diğer Sunucularla çakışmaması gerektiğine dikkat edin',
+ npxHelper: 'npx veya ikili dosya ile başlatılan mcp için uygundur',
+ uvxHelper: 'uvx ile başlatılan mcp için uygundur',
},
},
container: {
diff --git a/frontend/src/lang/modules/zh-Hant.ts b/frontend/src/lang/modules/zh-Hant.ts
index ae6a3e566..168b0d1b7 100644
--- a/frontend/src/lang/modules/zh-Hant.ts
+++ b/frontend/src/lang/modules/zh-Hant.ts
@@ -671,7 +671,6 @@ const message = {
server: 'MCP Server',
create: '创建 MCP Server',
edit: '編輯 MCP Server',
- commandHelper: '例如:npx -y {0}',
baseUrl: '外部訪問路徑',
baseUrlHelper: '例如:http://192.168.1.2:8000',
ssePath: 'SSE 路徑',
@@ -691,6 +690,8 @@ const message = {
outputTransport: '輸出類型',
streamableHttpPath: '流式傳輸路徑',
streamableHttpPathHelper: '例如:/mcp, 注意不要與其他 Server 重複',
+ npxHelper: '適合 npx 或者 二進制啟動的 mcp',
+ uvxHelper: '適合 uvx 啟動的 mcp',
},
},
container: {
diff --git a/frontend/src/lang/modules/zh.ts b/frontend/src/lang/modules/zh.ts
index 102b6f789..3e74f48b1 100644
--- a/frontend/src/lang/modules/zh.ts
+++ b/frontend/src/lang/modules/zh.ts
@@ -670,7 +670,6 @@ const message = {
server: 'MCP Server',
create: '创建 MCP Server',
edit: '编辑 MCP Server',
- commandHelper: '例如:npx -y {0}',
baseUrl: '外部访问路径',
baseUrlHelper: '例如:http://192.168.1.1:8000',
ssePath: 'SSE 路径',
@@ -690,6 +689,8 @@ const message = {
outputTransport: '输出类型',
streamableHttpPath: '流式传输路径',
streamableHttpPathHelper: '例如:/mcp, 注意不要与其他 Server 重复',
+ npxHelper: '适合 npx 或者 二进制启动的 mcp',
+ uvxHelper: '适合 uvx 启动的 mcp',
},
},
container: {
diff --git a/frontend/src/views/ai/mcp/server/operate/index.vue b/frontend/src/views/ai/mcp/server/operate/index.vue
index 0c9d2037d..d46b2a6b6 100644
--- a/frontend/src/views/ai/mcp/server/operate/index.vue
+++ b/frontend/src/views/ai/mcp/server/operate/index.vue
@@ -22,16 +22,27 @@