Compare commits

..

4 Commits

Author SHA1 Message Date
b5b2c32477 fix: 更新 useSkills
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 14:46:05 +08:00
11e26601be feat: 更新 Tools 页面
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 14:45:56 +08:00
298ff7c79d feat: 后端工具和 MCP 模块更新
- main.go: 更新工作空间目录,添加 MCP 模型迁移
- tool.go: 添加 description_cn 字段
- auth_service, tool_service: 优化
- 更新 Swagger 文档

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 14:45:49 +08:00
cab6488d71 feat: 新增 MCP 模块
- mcp_handler.go, mcp.go, mcp_repo.go, mcp_service.go

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 14:45:42 +08:00
13 changed files with 2113 additions and 33 deletions

View File

@@ -128,8 +128,8 @@ func ensureAdminWorkspace() {
return
}
// 创建子目录
for _, dir := range []string{"projects", "files", "temp"} {
// 创建子目录: skills(技能), scripts(脚本), sandbox(沙盒), files(文件), temp(临时)
for _, dir := range []string{"skills", "scripts", "sandbox", "files", "temp"} {
os.MkdirAll(filepath.Join(workspacePath, dir), 0755)
}
@@ -151,7 +151,7 @@ func main() {
}
// 3. 自动迁移表
db.AutoMigrate(&model.DatabaseInfo{}, &model.SubTableInfo{}, &model.ModelInfo{}, &model.KnowledgeBase{}, &model.KnowledgeDocument{}, &model.User{}, &model.Role{}, &model.Tool{})
db.AutoMigrate(&model.DatabaseInfo{}, &model.SubTableInfo{}, &model.ModelInfo{}, &model.KnowledgeBase{}, &model.KnowledgeDocument{}, &model.User{}, &model.Role{}, &model.Tool{}, &model.MCP{})
// 3.1 确保 users 和 roles 表存在(使用 SQL 强制创建)
db.Exec(`
@@ -209,6 +209,22 @@ func main() {
if !migrator.HasColumn(&model.Tool{}, "parameters") {
migrator.AddColumn(&model.Tool{}, "parameters")
}
if !migrator.HasColumn(&model.Tool{}, "description_cn") {
migrator.AddColumn(&model.Tool{}, "description_cn")
}
// MCP 相关字段
if !migrator.HasColumn(&model.Tool{}, "transport") {
migrator.AddColumn(&model.Tool{}, "transport")
}
if !migrator.HasColumn(&model.Tool{}, "command") {
migrator.AddColumn(&model.Tool{}, "command")
}
if !migrator.HasColumn(&model.Tool{}, "args") {
migrator.AddColumn(&model.Tool{}, "args")
}
if !migrator.HasColumn(&model.Tool{}, "env") {
migrator.AddColumn(&model.Tool{}, "env")
}
log.Println("Database tables verified/created")
@@ -219,6 +235,7 @@ func main() {
knowledgeRepo := repository.NewKnowledgeRepository(db)
userRepo := repository.NewUserRepository(db)
toolRepo := repository.NewToolRepository(db)
mcpRepo := repository.NewMCPRepository(db)
// 4.1 初始化默认管理员用户
initDefaultAdmin(userRepo)
@@ -235,6 +252,7 @@ func main() {
knowledgeService := service.NewKnowledgeService(knowledgeRepo, modelRepo, uploadService, cfg.PythonServiceURL, cfg.AICoreServiceAddr, cfg.MarkdownLocalPath)
authService := service.NewAuthService(cfg.JWTSecret, userRepo)
toolService := service.NewToolService(toolRepo)
mcpService := service.NewMCPService(mcpRepo)
// 4.2 初始化默认工具
if err := toolService.InitDefaultTools(); err != nil {
@@ -252,6 +270,7 @@ func main() {
knowledgeHandler := handler.NewKnowledgeHandler(knowledgeService)
authHandler := handler.NewAuthHandler(authService)
toolHandler := handler.NewToolHandler(toolService)
mcpHandler := handler.NewMCPHandler(mcpService)
var uploadHandler *handler.UploadHandler
if uploadService != nil {
uploadHandler = handler.NewUploadHandler(uploadService, knowledgeRepo)
@@ -396,6 +415,16 @@ func main() {
toolGroup.DELETE("/:id", toolHandler.Delete)
}
// MCP管理模块
mcpGroup := r.Group("/mcp")
{
mcpGroup.GET("/list", mcpHandler.List)
mcpGroup.GET("/:id", mcpHandler.GetByID)
mcpGroup.POST("/add", mcpHandler.Create)
mcpGroup.PUT("/:id", mcpHandler.Update)
mcpGroup.DELETE("/:id", mcpHandler.Delete)
}
// Swagger 文档
r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))

View File

@@ -1166,6 +1166,177 @@ const docTemplate = `{
}
}
},
"/mcp/add": {
"post": {
"description": "创建新的MCP工具配置",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"MCP管理"
],
"summary": "创建MCP",
"parameters": [
{
"description": "MCP信息",
"name": "mcp",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/model.MCP"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "object",
"additionalProperties": true
}
}
}
}
},
"/mcp/list": {
"get": {
"description": "获取所有MCP工具配置",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"MCP管理"
],
"summary": "获取MCP列表",
"parameters": [
{
"type": "string",
"description": "分类",
"name": "category",
"in": "query"
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "object",
"additionalProperties": true
}
}
}
}
},
"/mcp/{id}": {
"get": {
"description": "根据ID获取MCP配置",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"MCP管理"
],
"summary": "获取MCP详情",
"parameters": [
{
"type": "string",
"description": "MCP ID",
"name": "id",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "object",
"additionalProperties": true
}
}
}
},
"put": {
"description": "更新MCP配置",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"MCP管理"
],
"summary": "更新MCP",
"parameters": [
{
"type": "string",
"description": "MCP ID",
"name": "id",
"in": "path",
"required": true
},
{
"description": "MCP信息",
"name": "mcp",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/model.MCP"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "object",
"additionalProperties": true
}
}
}
},
"delete": {
"description": "删除MCP配置",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"MCP管理"
],
"summary": "删除MCP",
"parameters": [
{
"type": "string",
"description": "MCP ID",
"name": "id",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "object",
"additionalProperties": true
}
}
}
}
},
"/model/add": {
"post": {
"description": "添加新的AI模型配置",
@@ -2007,6 +2178,292 @@ const docTemplate = `{
}
}
}
},
"/tool/add": {
"post": {
"description": "创建新的工具",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"工具管理"
],
"summary": "创建工具",
"parameters": [
{
"description": "工具信息",
"name": "tool",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/model.Tool"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "object",
"additionalProperties": true
}
}
}
}
},
"/tool/list": {
"get": {
"description": "获取所有工具列表,支持按分类和状态筛选",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"工具管理"
],
"summary": "获取工具列表",
"parameters": [
{
"type": "string",
"description": "工具分类",
"name": "category",
"in": "query"
},
{
"type": "string",
"description": "工具状态",
"name": "status",
"in": "query"
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "object",
"additionalProperties": true
}
}
}
}
},
"/tool/sync": {
"get": {
"description": "从代码中的默认配置同步工具到数据库",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"工具管理"
],
"summary": "手动同步工具",
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "object",
"additionalProperties": true
}
}
}
}
},
"/tool/{id}": {
"get": {
"description": "根据ID获取工具详情",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"工具管理"
],
"summary": "获取工具详情",
"parameters": [
{
"type": "string",
"description": "工具ID",
"name": "id",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "object",
"additionalProperties": true
}
}
}
},
"put": {
"description": "更新工具信息",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"工具管理"
],
"summary": "更新工具",
"parameters": [
{
"type": "string",
"description": "工具ID",
"name": "id",
"in": "path",
"required": true
},
{
"description": "工具信息",
"name": "tool",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/model.Tool"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "object",
"additionalProperties": true
}
}
}
},
"delete": {
"description": "删除工具",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"工具管理"
],
"summary": "删除工具",
"parameters": [
{
"type": "string",
"description": "工具ID",
"name": "id",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "object",
"additionalProperties": true
}
}
}
}
},
"/user/list": {
"get": {
"security": [
{
"BearerAuth": []
}
],
"description": "获取所有用户列表(需要管理员权限)",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"用户管理"
],
"summary": "获取所有用户",
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "object",
"additionalProperties": true
}
},
"401": {
"description": "Unauthorized",
"schema": {
"type": "object",
"additionalProperties": {
"type": "string"
}
}
}
}
}
},
"/user/{id}": {
"get": {
"security": [
{
"BearerAuth": []
}
],
"description": "根据ID获取用户详情",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"用户管理"
],
"summary": "获取用户详情",
"parameters": [
{
"type": "string",
"description": "用户ID",
"name": "id",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "object",
"additionalProperties": true
}
},
"404": {
"description": "Not Found",
"schema": {
"type": "object",
"additionalProperties": {
"type": "string"
}
}
}
}
}
}
},
"definitions": {
@@ -2363,6 +2820,54 @@ const docTemplate = `{
}
}
},
"model.MCP": {
"type": "object",
"properties": {
"args": {
"description": "参数JSON数组格式",
"type": "string"
},
"category": {
"description": "分类",
"type": "string"
},
"command": {
"description": "启动命令",
"type": "string"
},
"created_at": {
"type": "string"
},
"description": {
"description": "英文描述",
"type": "string"
},
"description_cn": {
"description": "中文描述",
"type": "string"
},
"env": {
"description": "环境变量JSON对象格式",
"type": "string"
},
"id": {
"type": "string"
},
"name": {
"type": "string"
},
"status": {
"type": "string"
},
"transport": {
"description": "stdio, http, sse",
"type": "string"
},
"updated_at": {
"type": "string"
}
}
},
"model.MemoryInfo": {
"type": "object",
"properties": {
@@ -2772,6 +3277,67 @@ const docTemplate = `{
}
}
},
"model.Tool": {
"type": "object",
"properties": {
"args": {
"description": "参数JSON数组格式",
"type": "string"
},
"category": {
"type": "string"
},
"command": {
"description": "启动命令",
"type": "string"
},
"created_at": {
"type": "string"
},
"description": {
"description": "英文描述",
"type": "string"
},
"description_cn": {
"description": "中文描述",
"type": "string"
},
"env": {
"description": "环境变量JSON对象格式",
"type": "string"
},
"id": {
"type": "string"
},
"name": {
"type": "string"
},
"parameters": {
"description": "JSON格式存储",
"type": "string"
},
"provider": {
"type": "string"
},
"require_approval": {
"type": "boolean"
},
"security_level": {
"type": "string"
},
"status": {
"description": "状态",
"type": "string"
},
"transport": {
"description": "MCP 特有字段",
"type": "string"
},
"updated_at": {
"type": "string"
}
}
},
"model.UpdateDatabaseRequest": {
"type": "object",
"properties": {

View File

@@ -1155,6 +1155,177 @@
}
}
},
"/mcp/add": {
"post": {
"description": "创建新的MCP工具配置",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"MCP管理"
],
"summary": "创建MCP",
"parameters": [
{
"description": "MCP信息",
"name": "mcp",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/model.MCP"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "object",
"additionalProperties": true
}
}
}
}
},
"/mcp/list": {
"get": {
"description": "获取所有MCP工具配置",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"MCP管理"
],
"summary": "获取MCP列表",
"parameters": [
{
"type": "string",
"description": "分类",
"name": "category",
"in": "query"
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "object",
"additionalProperties": true
}
}
}
}
},
"/mcp/{id}": {
"get": {
"description": "根据ID获取MCP配置",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"MCP管理"
],
"summary": "获取MCP详情",
"parameters": [
{
"type": "string",
"description": "MCP ID",
"name": "id",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "object",
"additionalProperties": true
}
}
}
},
"put": {
"description": "更新MCP配置",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"MCP管理"
],
"summary": "更新MCP",
"parameters": [
{
"type": "string",
"description": "MCP ID",
"name": "id",
"in": "path",
"required": true
},
{
"description": "MCP信息",
"name": "mcp",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/model.MCP"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "object",
"additionalProperties": true
}
}
}
},
"delete": {
"description": "删除MCP配置",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"MCP管理"
],
"summary": "删除MCP",
"parameters": [
{
"type": "string",
"description": "MCP ID",
"name": "id",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "object",
"additionalProperties": true
}
}
}
}
},
"/model/add": {
"post": {
"description": "添加新的AI模型配置",
@@ -1996,6 +2167,292 @@
}
}
}
},
"/tool/add": {
"post": {
"description": "创建新的工具",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"工具管理"
],
"summary": "创建工具",
"parameters": [
{
"description": "工具信息",
"name": "tool",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/model.Tool"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "object",
"additionalProperties": true
}
}
}
}
},
"/tool/list": {
"get": {
"description": "获取所有工具列表,支持按分类和状态筛选",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"工具管理"
],
"summary": "获取工具列表",
"parameters": [
{
"type": "string",
"description": "工具分类",
"name": "category",
"in": "query"
},
{
"type": "string",
"description": "工具状态",
"name": "status",
"in": "query"
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "object",
"additionalProperties": true
}
}
}
}
},
"/tool/sync": {
"get": {
"description": "从代码中的默认配置同步工具到数据库",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"工具管理"
],
"summary": "手动同步工具",
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "object",
"additionalProperties": true
}
}
}
}
},
"/tool/{id}": {
"get": {
"description": "根据ID获取工具详情",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"工具管理"
],
"summary": "获取工具详情",
"parameters": [
{
"type": "string",
"description": "工具ID",
"name": "id",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "object",
"additionalProperties": true
}
}
}
},
"put": {
"description": "更新工具信息",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"工具管理"
],
"summary": "更新工具",
"parameters": [
{
"type": "string",
"description": "工具ID",
"name": "id",
"in": "path",
"required": true
},
{
"description": "工具信息",
"name": "tool",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/model.Tool"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "object",
"additionalProperties": true
}
}
}
},
"delete": {
"description": "删除工具",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"工具管理"
],
"summary": "删除工具",
"parameters": [
{
"type": "string",
"description": "工具ID",
"name": "id",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "object",
"additionalProperties": true
}
}
}
}
},
"/user/list": {
"get": {
"security": [
{
"BearerAuth": []
}
],
"description": "获取所有用户列表(需要管理员权限)",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"用户管理"
],
"summary": "获取所有用户",
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "object",
"additionalProperties": true
}
},
"401": {
"description": "Unauthorized",
"schema": {
"type": "object",
"additionalProperties": {
"type": "string"
}
}
}
}
}
},
"/user/{id}": {
"get": {
"security": [
{
"BearerAuth": []
}
],
"description": "根据ID获取用户详情",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"用户管理"
],
"summary": "获取用户详情",
"parameters": [
{
"type": "string",
"description": "用户ID",
"name": "id",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "object",
"additionalProperties": true
}
},
"404": {
"description": "Not Found",
"schema": {
"type": "object",
"additionalProperties": {
"type": "string"
}
}
}
}
}
}
},
"definitions": {
@@ -2352,6 +2809,54 @@
}
}
},
"model.MCP": {
"type": "object",
"properties": {
"args": {
"description": "参数JSON数组格式",
"type": "string"
},
"category": {
"description": "分类",
"type": "string"
},
"command": {
"description": "启动命令",
"type": "string"
},
"created_at": {
"type": "string"
},
"description": {
"description": "英文描述",
"type": "string"
},
"description_cn": {
"description": "中文描述",
"type": "string"
},
"env": {
"description": "环境变量JSON对象格式",
"type": "string"
},
"id": {
"type": "string"
},
"name": {
"type": "string"
},
"status": {
"type": "string"
},
"transport": {
"description": "stdio, http, sse",
"type": "string"
},
"updated_at": {
"type": "string"
}
}
},
"model.MemoryInfo": {
"type": "object",
"properties": {
@@ -2761,6 +3266,67 @@
}
}
},
"model.Tool": {
"type": "object",
"properties": {
"args": {
"description": "参数JSON数组格式",
"type": "string"
},
"category": {
"type": "string"
},
"command": {
"description": "启动命令",
"type": "string"
},
"created_at": {
"type": "string"
},
"description": {
"description": "英文描述",
"type": "string"
},
"description_cn": {
"description": "中文描述",
"type": "string"
},
"env": {
"description": "环境变量JSON对象格式",
"type": "string"
},
"id": {
"type": "string"
},
"name": {
"type": "string"
},
"parameters": {
"description": "JSON格式存储",
"type": "string"
},
"provider": {
"type": "string"
},
"require_approval": {
"type": "boolean"
},
"security_level": {
"type": "string"
},
"status": {
"description": "状态",
"type": "string"
},
"transport": {
"description": "MCP 特有字段",
"type": "string"
},
"updated_at": {
"type": "string"
}
}
},
"model.UpdateDatabaseRequest": {
"type": "object",
"properties": {

View File

@@ -245,6 +245,40 @@ definitions:
description: 中文映射名
type: string
type: object
model.MCP:
properties:
args:
description: 参数JSON数组格式
type: string
category:
description: 分类
type: string
command:
description: 启动命令
type: string
created_at:
type: string
description:
description: 英文描述
type: string
description_cn:
description: 中文描述
type: string
env:
description: 环境变量JSON对象格式
type: string
id:
type: string
name:
type: string
status:
type: string
transport:
description: stdio, http, sse
type: string
updated_at:
type: string
type: object
model.MemoryInfo:
properties:
available:
@@ -536,6 +570,49 @@ definitions:
- model_type
- provider
type: object
model.Tool:
properties:
args:
description: 参数JSON数组格式
type: string
category:
type: string
command:
description: 启动命令
type: string
created_at:
type: string
description:
description: 英文描述
type: string
description_cn:
description: 中文描述
type: string
env:
description: 环境变量JSON对象格式
type: string
id:
type: string
name:
type: string
parameters:
description: JSON格式存储
type: string
provider:
type: string
require_approval:
type: boolean
security_level:
type: string
status:
description: 状态
type: string
transport:
description: MCP 特有字段
type: string
updated_at:
type: string
type: object
model.UpdateDatabaseRequest:
properties:
charset:
@@ -1399,6 +1476,120 @@ paths:
summary: 获取数据库列表
tags:
- 数据库管理
/mcp/{id}:
delete:
consumes:
- application/json
description: 删除MCP配置
parameters:
- description: MCP ID
in: path
name: id
required: true
type: string
produces:
- application/json
responses:
"200":
description: OK
schema:
additionalProperties: true
type: object
summary: 删除MCP
tags:
- MCP管理
get:
consumes:
- application/json
description: 根据ID获取MCP配置
parameters:
- description: MCP ID
in: path
name: id
required: true
type: string
produces:
- application/json
responses:
"200":
description: OK
schema:
additionalProperties: true
type: object
summary: 获取MCP详情
tags:
- MCP管理
put:
consumes:
- application/json
description: 更新MCP配置
parameters:
- description: MCP ID
in: path
name: id
required: true
type: string
- description: MCP信息
in: body
name: mcp
required: true
schema:
$ref: '#/definitions/model.MCP'
produces:
- application/json
responses:
"200":
description: OK
schema:
additionalProperties: true
type: object
summary: 更新MCP
tags:
- MCP管理
/mcp/add:
post:
consumes:
- application/json
description: 创建新的MCP工具配置
parameters:
- description: MCP信息
in: body
name: mcp
required: true
schema:
$ref: '#/definitions/model.MCP'
produces:
- application/json
responses:
"200":
description: OK
schema:
additionalProperties: true
type: object
summary: 创建MCP
tags:
- MCP管理
/mcp/list:
get:
consumes:
- application/json
description: 获取所有MCP工具配置
parameters:
- description: 分类
in: query
name: category
type: string
produces:
- application/json
responses:
"200":
description: OK
schema:
additionalProperties: true
type: object
summary: 获取MCP列表
tags:
- MCP管理
/model/{id}:
delete:
consumes:
@@ -1957,4 +2148,192 @@ paths:
summary: 获取系统信息
tags:
- 系统
/tool/{id}:
delete:
consumes:
- application/json
description: 删除工具
parameters:
- description: 工具ID
in: path
name: id
required: true
type: string
produces:
- application/json
responses:
"200":
description: OK
schema:
additionalProperties: true
type: object
summary: 删除工具
tags:
- 工具管理
get:
consumes:
- application/json
description: 根据ID获取工具详情
parameters:
- description: 工具ID
in: path
name: id
required: true
type: string
produces:
- application/json
responses:
"200":
description: OK
schema:
additionalProperties: true
type: object
summary: 获取工具详情
tags:
- 工具管理
put:
consumes:
- application/json
description: 更新工具信息
parameters:
- description: 工具ID
in: path
name: id
required: true
type: string
- description: 工具信息
in: body
name: tool
required: true
schema:
$ref: '#/definitions/model.Tool'
produces:
- application/json
responses:
"200":
description: OK
schema:
additionalProperties: true
type: object
summary: 更新工具
tags:
- 工具管理
/tool/add:
post:
consumes:
- application/json
description: 创建新的工具
parameters:
- description: 工具信息
in: body
name: tool
required: true
schema:
$ref: '#/definitions/model.Tool'
produces:
- application/json
responses:
"200":
description: OK
schema:
additionalProperties: true
type: object
summary: 创建工具
tags:
- 工具管理
/tool/list:
get:
consumes:
- application/json
description: 获取所有工具列表,支持按分类和状态筛选
parameters:
- description: 工具分类
in: query
name: category
type: string
- description: 工具状态
in: query
name: status
type: string
produces:
- application/json
responses:
"200":
description: OK
schema:
additionalProperties: true
type: object
summary: 获取工具列表
tags:
- 工具管理
/tool/sync:
get:
consumes:
- application/json
description: 从代码中的默认配置同步工具到数据库
produces:
- application/json
responses:
"200":
description: OK
schema:
additionalProperties: true
type: object
summary: 手动同步工具
tags:
- 工具管理
/user/{id}:
get:
consumes:
- application/json
description: 根据ID获取用户详情
parameters:
- description: 用户ID
in: path
name: id
required: true
type: string
produces:
- application/json
responses:
"200":
description: OK
schema:
additionalProperties: true
type: object
"404":
description: Not Found
schema:
additionalProperties:
type: string
type: object
security:
- BearerAuth: []
summary: 获取用户详情
tags:
- 用户管理
/user/list:
get:
consumes:
- application/json
description: 获取所有用户列表(需要管理员权限)
produces:
- application/json
responses:
"200":
description: OK
schema:
additionalProperties: true
type: object
"401":
description: Unauthorized
schema:
additionalProperties:
type: string
type: object
security:
- BearerAuth: []
summary: 获取所有用户
tags:
- 用户管理
swagger: "2.0"

View File

@@ -0,0 +1,154 @@
package handler
import (
"net/http"
"x-agents/server/internal/model"
"x-agents/server/internal/service"
"github.com/gin-gonic/gin"
)
// MCPHandler MCP工具处理器
type MCPHandler struct {
mcpService *service.MCPService
}
// NewMCPHandler 创建MCP处理器
func NewMCPHandler(mcpService *service.MCPService) *MCPHandler {
return &MCPHandler{mcpService: mcpService}
}
// List 获取MCP列表
// @Summary 获取MCP列表
// @Description 获取所有MCP工具配置
// @Tags MCP管理
// @Accept json
// @Produce json
// @Param category query string false "分类"
// @Success 200 {object} map[string]interface{}
// @Router /mcp/list [get]
func (h *MCPHandler) List(c *gin.Context) {
category := c.Query("category")
var mcps []model.MCP
var err error
if category != "" {
mcps, err = h.mcpService.GetMCPsByCategory(category)
} else {
mcps, err = h.mcpService.GetAllMCPs()
}
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, gin.H{"list": mcps, "total": len(mcps)})
}
// GetByID 获取MCP详情
// @Summary 获取MCP详情
// @Description 根据ID获取MCP配置
// @Tags MCP管理
// @Accept json
// @Produce json
// @Param id path string true "MCP ID"
// @Success 200 {object} map[string]interface{}
// @Router /mcp/{id} [get]
func (h *MCPHandler) GetByID(c *gin.Context) {
id := c.Param("id")
if id == "" {
c.JSON(http.StatusBadRequest, gin.H{"error": "mcp id is required"})
return
}
mcp, err := h.mcpService.GetMCPByID(id)
if err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": "mcp not found"})
return
}
c.JSON(http.StatusOK, gin.H{"mcp": mcp})
}
// Create 创建MCP
// @Summary 创建MCP
// @Description 创建新的MCP工具配置
// @Tags MCP管理
// @Accept json
// @Produce json
// @Param mcp body model.MCP true "MCP信息"
// @Success 200 {object} map[string]interface{}
// @Router /mcp/add [post]
func (h *MCPHandler) Create(c *gin.Context) {
var mcp model.MCP
if err := c.ShouldBindJSON(&mcp); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
if err := h.mcpService.CreateMCP(&mcp); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, gin.H{"message": "mcp created", "mcp": mcp})
}
// Update 更新MCP
// @Summary 更新MCP
// @Description 更新MCP配置
// @Tags MCP管理
// @Accept json
// @Produce json
// @Param id path string true "MCP ID"
// @Param mcp body model.MCP true "MCP信息"
// @Success 200 {object} map[string]interface{}
// @Router /mcp/{id} [put]
func (h *MCPHandler) Update(c *gin.Context) {
id := c.Param("id")
if id == "" {
c.JSON(http.StatusBadRequest, gin.H{"error": "mcp id is required"})
return
}
var mcp model.MCP
if err := c.ShouldBindJSON(&mcp); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
mcp.ID = id
if err := h.mcpService.UpdateMCP(&mcp); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, gin.H{"message": "mcp updated"})
}
// Delete 删除MCP
// @Summary 删除MCP
// @Description 删除MCP配置
// @Tags MCP管理
// @Accept json
// @Produce json
// @Param id path string true "MCP ID"
// @Success 200 {object} map[string]interface{}
// @Router /mcp/{id} [delete]
func (h *MCPHandler) Delete(c *gin.Context) {
id := c.Param("id")
if id == "" {
c.JSON(http.StatusBadRequest, gin.H{"error": "mcp id is required"})
return
}
if err := h.mcpService.DeleteMCP(id); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, gin.H{"message": "mcp deleted"})
}

View File

@@ -0,0 +1,32 @@
package model
import (
"time"
"github.com/google/uuid"
"gorm.io/gorm"
)
// MCP MCP工具配置
type MCP struct {
ID string `json:"id" gorm:"primaryKey"`
Name string `json:"name" gorm:"uniqueIndex;size:100;not null"`
Description string `json:"description" gorm:"type:text"` // 英文描述
DescriptionCN string `json:"description_cn" gorm:"type:text"` // 中文描述
Category string `json:"category" gorm:"size:50;not null"` // 分类
Transport string `json:"transport" gorm:"size:20;default:'stdio'"` // stdio, http, sse
Command string `json:"command" gorm:"size:500"` // 启动命令
Args string `json:"args" gorm:"type:text"` // 参数JSON数组格式
Env string `json:"env" gorm:"type:text"` // 环境变量JSON对象格式
Status string `json:"status" gorm:"size:20;default:'active'"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
// BeforeCreate 创建前自动生成ID
func (m *MCP) BeforeCreate(tx *gorm.DB) error {
if m.ID == "" {
m.ID = uuid.New().String()
}
return nil
}

View File

@@ -11,12 +11,19 @@ import (
type Tool struct {
ID string `json:"id" gorm:"primaryKey"`
Name string `json:"name" gorm:"uniqueIndex;size:100;not null"`
Description string `json:"description" gorm:"type:text"`
Description string `json:"description" gorm:"type:text"` // 英文描述
DescriptionCN string `json:"description_cn" gorm:"type:text"` // 中文描述
Category string `json:"category" gorm:"size:50;not null"`
Provider string `json:"provider" gorm:"size:100"`
SecurityLevel string `json:"security_level" gorm:"size:20;default:'safe'"`
RequireApproval bool `json:"require_approval" gorm:"default:false"`
Parameters string `json:"parameters" gorm:"type:text"` // JSON格式存储
// MCP 特有字段
Transport string `json:"transport" gorm:"size:20;default:'stdio'"` // stdio, http, sse
Command string `json:"command" gorm:"size:500"` // 启动命令
Args string `json:"args" gorm:"type:text"` // 参数JSON数组格式
Env string `json:"env" gorm:"type:text"` // 环境变量JSON对象格式
// 状态
Status string `json:"status" gorm:"size:20;default:'active'"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`

View File

@@ -0,0 +1,67 @@
package repository
import (
"x-agents/server/internal/model"
"gorm.io/gorm"
)
type MCPRepository struct {
db *gorm.DB
}
func NewMCPRepository(db *gorm.DB) *MCPRepository {
return &MCPRepository{db: db}
}
func (r *MCPRepository) DB() *gorm.DB {
return r.db
}
func (r *MCPRepository) Create(mcp *model.MCP) error {
return r.db.Create(mcp).Error
}
func (r *MCPRepository) FindAll() ([]model.MCP, error) {
var mcps []model.MCP
err := r.db.Order("created_at DESC").Find(&mcps).Error
return mcps, err
}
func (r *MCPRepository) FindByID(id string) (*model.MCP, error) {
var mcp model.MCP
err := r.db.First(&mcp, "id = ?", id).Error
if err != nil {
return nil, err
}
return &mcp, nil
}
func (r *MCPRepository) FindByName(name string) (*model.MCP, error) {
var mcp model.MCP
err := r.db.First(&mcp, "name = ?", name).Error
if err != nil {
return nil, err
}
return &mcp, nil
}
func (r *MCPRepository) FindByCategory(category string) ([]model.MCP, error) {
var mcps []model.MCP
err := r.db.Where("category = ?", category).Find(&mcps).Error
return mcps, err
}
func (r *MCPRepository) Update(mcp *model.MCP) error {
return r.db.Save(mcp).Error
}
func (r *MCPRepository) Delete(id string) error {
return r.db.Delete(&model.MCP{}, "id = ?", id).Error
}
func (r *MCPRepository) Count() (int64, error) {
var count int64
err := r.db.Model(&model.MCP{}).Count(&count).Error
return count, err
}

View File

@@ -193,8 +193,8 @@ func (s *AuthService) createUserWorkspace(username string) error {
return err
}
// 创建子目录
subDirs := []string{"projects", "files", "temp"}
// 创建子目录: skills(技能), scripts(脚本), sandbox(沙盒), files(文件), temp(临时)
subDirs := []string{"skills", "scripts", "sandbox", "files", "temp"}
for _, dir := range subDirs {
if err := os.MkdirAll(filepath.Join(workspacePath, dir), 0755); err != nil {
return err

View File

@@ -0,0 +1,42 @@
package service
import (
"x-agents/server/internal/model"
"x-agents/server/internal/repository"
)
type MCPService struct {
mcpRepo *repository.MCPRepository
}
func NewMCPService(mcpRepo *repository.MCPRepository) *MCPService {
return &MCPService{mcpRepo: mcpRepo}
}
func (s *MCPService) GetAllMCPs() ([]model.MCP, error) {
return s.mcpRepo.FindAll()
}
func (s *MCPService) GetMCPByID(id string) (*model.MCP, error) {
return s.mcpRepo.FindByID(id)
}
func (s *MCPService) GetMCPByName(name string) (*model.MCP, error) {
return s.mcpRepo.FindByName(name)
}
func (s *MCPService) GetMCPsByCategory(category string) ([]model.MCP, error) {
return s.mcpRepo.FindByCategory(category)
}
func (s *MCPService) CreateMCP(mcp *model.MCP) error {
return s.mcpRepo.Create(mcp)
}
func (s *MCPService) UpdateMCP(mcp *model.MCP) error {
return s.mcpRepo.Update(mcp)
}
func (s *MCPService) DeleteMCP(id string) error {
return s.mcpRepo.Delete(id)
}

View File

@@ -58,12 +58,12 @@ func (s *ToolService) DeleteTool(id string) error {
func (s *ToolService) InitDefaultTools() error {
log.Println("[ToolService] Starting init default tools...")
// 获取默认工具
tools := s.getDefaultTools()
// 删除现有的系统工具,重新插入
s.toolRepo.DB().Where("provider = ?", "system").Delete(&model.Tool{})
log.Printf("[ToolService] Deleted existing system tools, inserting %d default tools...", len(tools))
// 插入默认工具
tools := s.getDefaultTools()
log.Printf("[ToolService] Inserting %d default tools...", len(tools))
for _, tool := range tools {
if err := s.toolRepo.Create(&tool); err != nil {
@@ -83,6 +83,7 @@ func (s *ToolService) getDefaultTools() []model.Tool {
{
Name: "read_file",
Description: "Read the contents of a file from the filesystem.",
DescriptionCN: "读取文件系统的文件内容。",
Category: "file",
SecurityLevel: "safe",
RequireApproval: false,
@@ -93,6 +94,7 @@ func (s *ToolService) getDefaultTools() []model.Tool {
{
Name: "write_file",
Description: "Write content to a file. Creates the file if it doesn't exist, overwrites if it does.",
DescriptionCN: "写入内容到文件。如果文件不存在则创建,存在则覆盖。",
Category: "file",
SecurityLevel: "review",
RequireApproval: true,
@@ -103,6 +105,7 @@ func (s *ToolService) getDefaultTools() []model.Tool {
{
Name: "list_dir",
Description: "List the contents of a directory.",
DescriptionCN: "列出目录的内容。",
Category: "file",
SecurityLevel: "safe",
RequireApproval: false,
@@ -113,6 +116,7 @@ func (s *ToolService) getDefaultTools() []model.Tool {
{
Name: "delete_file",
Description: "Delete a file or directory.",
DescriptionCN: "删除文件或目录。",
Category: "file",
SecurityLevel: "danger",
RequireApproval: true,
@@ -123,6 +127,7 @@ func (s *ToolService) getDefaultTools() []model.Tool {
{
Name: "search_files",
Description: "Search for files by name pattern or content.",
DescriptionCN: "按文件名模式或内容搜索文件。",
Category: "file",
SecurityLevel: "safe",
RequireApproval: false,
@@ -134,6 +139,7 @@ func (s *ToolService) getDefaultTools() []model.Tool {
{
Name: "execute_python",
Description: "Execute Python code in a sandboxed environment.",
DescriptionCN: "在沙盒环境中执行Python代码。",
Category: "executor",
SecurityLevel: "review",
RequireApproval: true,
@@ -144,6 +150,7 @@ func (s *ToolService) getDefaultTools() []model.Tool {
{
Name: "execute_javascript",
Description: "Execute JavaScript code in a sandboxed environment.",
DescriptionCN: "在沙盒环境中执行JavaScript代码。",
Category: "executor",
SecurityLevel: "review",
RequireApproval: true,
@@ -154,6 +161,7 @@ func (s *ToolService) getDefaultTools() []model.Tool {
{
Name: "execute_bash",
Description: "Execute a bash command in a sandboxed environment.",
DescriptionCN: "在沙盒环境中执行Bash命令。",
Category: "executor",
SecurityLevel: "danger",
RequireApproval: true,
@@ -165,6 +173,7 @@ func (s *ToolService) getDefaultTools() []model.Tool {
{
Name: "web_fetch",
Description: "Fetch content from a web URL.",
DescriptionCN: "从网页URL获取内容。",
Category: "web",
SecurityLevel: "safe",
RequireApproval: false,
@@ -175,6 +184,7 @@ func (s *ToolService) getDefaultTools() []model.Tool {
{
Name: "web_search",
Description: "Search the web for information.",
DescriptionCN: "在网络上搜索信息。",
Category: "web",
SecurityLevel: "safe",
RequireApproval: false,
@@ -186,6 +196,7 @@ func (s *ToolService) getDefaultTools() []model.Tool {
{
Name: "http_request",
Description: "Make HTTP requests to APIs.",
DescriptionCN: "向API发送HTTP请求。",
Category: "http",
SecurityLevel: "safe",
RequireApproval: false,
@@ -197,6 +208,7 @@ func (s *ToolService) getDefaultTools() []model.Tool {
{
Name: "send_notification",
Description: "Send notifications via email, webhook, dingtalk, or slack.",
DescriptionCN: "通过邮件、webhook、钉钉或Slack发送通知。",
Category: "notification",
SecurityLevel: "safe",
RequireApproval: false,
@@ -208,6 +220,7 @@ func (s *ToolService) getDefaultTools() []model.Tool {
{
Name: "get_current_time",
Description: "Get the current date and time.",
DescriptionCN: "获取当前日期和时间。",
Category: "system",
SecurityLevel: "safe",
RequireApproval: false,

View File

@@ -28,7 +28,7 @@ import { useTools } from './tools/useTools'
import '@/views/database/database.css'
// 使用工具 composable
const { tools, toolsLoading, fetchTools, syncTools, deleteTool: deleteToolApi } = useTools()
const { tools, toolsLoading, fetchTools, syncTools, deleteTool: deleteToolApi, createTool } = useTools()
// 图标组件映射
const iconComponents: Record<string, any> = {
@@ -65,7 +65,8 @@ interface Tool {
parameters: string
status: string
type: 'built-in' | 'mcp'
createdAt?: string
icon?: string
created_at?: string
}
// 页面加载时获取工具列表
@@ -92,6 +93,7 @@ const editForm = ref({
name: '',
description: '',
provider: '',
icon: '',
})
// Statistics
@@ -123,7 +125,7 @@ const tabCounts = computed(() => ({
const openEdit = (tool: any) => {
editingTool.value = tool
editForm.value = { name: tool.name, description: tool.description, provider: tool.provider || '' }
editForm.value = { name: tool.name, description: tool.description, provider: tool.provider || '', icon: tool.icon || '' }
isEditing.value = true
}
@@ -133,9 +135,32 @@ const saveEdit = () => {
const cancelEdit = () => { isEditing.value = false; editingTool.value = null }
const toggleStatus = async (tool: any) => {
// 状态切换确认弹窗
const showStatusConfirm = ref(false)
const toolToToggle = ref<any>(null)
const confirmToggleStatus = (tool: any) => {
toolToToggle.value = tool
showStatusConfirm.value = true
}
const toggleStatus = async () => {
if (!toolToToggle.value) return
const tool = toolToToggle.value
const newStatus = tool.status === 'active' ? 'inactive' : 'active'
const action = newStatus === 'active' ? 'activate' : 'deactivate'
// TODO: 调用 API 更新状态
console.log(`${action} tool:`, tool.name)
showStatusConfirm.value = false
toolToToggle.value = null
}
const cancelToggleStatus = () => {
showStatusConfirm.value = false
toolToToggle.value = null
}
const handleDeleteTool = async (id: string) => {
@@ -148,6 +173,67 @@ const handleDeleteTool = async (id: string) => {
const handleSyncTools = async () => {
await syncTools()
}
// Add MCP 弹窗
const showAddMcp = ref(false)
const mcpForm = ref({
name: '',
description: '',
description_cn: '',
transport: 'stdio',
command: '',
args: '',
env: '',
})
const openAddMcp = () => {
mcpForm.value = {
name: '',
description: '',
description_cn: '',
transport: 'stdio',
command: '',
args: '',
env: '',
}
showAddMcp.value = true
}
const closeAddMcp = () => {
showAddMcp.value = false
}
const submitMcp = async () => {
try {
// 组装参数
const argsArray = mcpForm.value.args ? mcpForm.value.args.split('\n').filter((a: string) => a.trim()) : []
const envObj: Record<string, string> = {}
if (mcpForm.value.env) {
mcpForm.value.env.split('\n').forEach((line: string) => {
const [key, ...valueParts] = line.split('=')
if (key && valueParts.length > 0) {
envObj[key.trim()] = valueParts.join('=').trim()
}
})
}
await createTool({
name: mcpForm.value.name,
description: mcpForm.value.description,
description_cn: mcpForm.value.description_cn,
category: 'mcp',
provider: 'mcp',
transport: mcpForm.value.transport,
command: mcpForm.value.command,
args: JSON.stringify(argsArray),
env: JSON.stringify(envObj),
status: 'active',
})
closeAddMcp()
} catch (error) {
console.error('Failed to add MCP:', error)
}
}
</script>
<template>
@@ -163,7 +249,7 @@ const handleSyncTools = async () => {
<i class="fa-solid fa-sync mr-1"></i>
Sync Tools
</button>
<button v-if="activeTab === 'mcp'" class="btn-primary">
<button v-if="activeTab === 'mcp'" @click="openAddMcp" class="btn-primary">
<Plus class="w-4 h-4 mr-1" />
Add MCP
</button>
@@ -247,15 +333,19 @@ const handleSyncTools = async () => {
<td class="px-5 py-4 text-center text-gray-400 text-sm">{{ tool.created_at || '-' }}</td>
<td class="px-5 py-4">
<div class="flex items-center justify-center gap-2">
<button @click="toggleStatus(tool)" class="btn-icon" :title="tool.status === 'active' ? 'Deactivate' : 'Activate'">
<!-- 激活/停用按钮 - 所有工具都有 -->
<button @click="confirmToggleStatus(tool)" class="btn-icon" :title="tool.status === 'active' ? 'Deactivate' : 'Activate'">
<component :is="tool.status === 'active' ? Pause : Play" class="w-4 h-4 text-gray-400 hover:text-white" />
</button>
<!-- 编辑和删除按钮 - 只有 MCP 工具才有 -->
<template v-if="tool.provider !== 'system'">
<button @click="openEdit(tool)" class="btn-icon" title="Edit">
<Edit class="w-4 h-4 text-gray-400 hover:text-white" />
</button>
<button @click="handleDeleteTool(tool.id)" class="btn-icon" title="Delete">
<Trash2 class="w-4 h-4 text-gray-400 hover:text-red-400" />
</button>
</template>
</div>
</td>
</tr>
@@ -292,6 +382,10 @@ const handleSyncTools = async () => {
<label class="block text-sm font-medium text-gray-300 mb-2">Provider</label>
<input v-model="editForm.provider" type="text" class="input-field">
</div>
<div>
<label class="block text-sm font-medium text-gray-300 mb-2">Icon</label>
<input v-model="editForm.icon" type="text" placeholder="e.g., FileText, Globe, Code" class="input-field">
</div>
<div>
<label class="block text-sm font-medium text-gray-300 mb-2">Description</label>
<textarea v-model="editForm.description" rows="3" class="input-field resize-none"></textarea>
@@ -305,6 +399,107 @@ const handleSyncTools = async () => {
</div>
</div>
</Teleport>
<!-- 状态切换确认弹窗 -->
<Teleport to="body">
<Transition name="fade">
<div v-if="showStatusConfirm" class="fixed inset-0 bg-black/60 flex items-center justify-center z-50" @click="cancelToggleStatus">
<div class="bg-dark-700 rounded-xl w-full max-w-sm border border-dark-500 shadow-2xl" @click.stop>
<div class="p-6 text-center">
<div class="w-14 h-14 rounded-full flex items-center justify-center mx-auto mb-4" :class="toolToToggle?.status === 'active' ? 'bg-yellow-500/20' : 'bg-green-500/20'">
<component :is="toolToToggle?.status === 'active' ? Pause : Play" class="text-xl" :class="toolToToggle?.status === 'active' ? 'text-yellow-400' : 'text-green-400'" />
</div>
<h3 class="text-lg font-semibold text-white mb-2">
{{ toolToToggle?.status === 'active' ? 'Stop Tool' : 'Activate Tool' }}
</h3>
<p class="text-gray-400 text-sm">
{{ toolToToggle?.status === 'active'
? `Are you sure you want to stop "${toolToToggle?.name}"? This tool will no longer be available.`
: `Are you sure you want to activate "${toolToToggle?.name}"? This tool will become available.`
}}
</p>
</div>
<div class="flex border-t border-dark-500">
<button @click="cancelToggleStatus" class="flex-1 py-3 text-gray-400 hover:text-white hover:bg-dark-600 transition-colors">
Cancel
</button>
<button @click="toggleStatus" class="flex-1 py-3 transition-colors border-l border-dark-500" :class="toolToToggle?.status === 'active' ? 'text-yellow-400 hover:bg-yellow-500/10' : 'text-green-400 hover:bg-green-500/10'">
{{ toolToToggle?.status === 'active' ? 'Stop' : 'Activate' }}
</button>
</div>
</div>
</div>
</Transition>
</Teleport>
<!-- Add MCP 弹窗 -->
<Teleport to="body">
<div v-if="showAddMcp" class="fixed inset-0 bg-black/60 flex items-center justify-center z-50 modal-overlay" @click="closeAddMcp">
<div class="bg-dark-700 rounded-2xl w-full max-w-xl border border-dark-500 shadow-2xl modal-content" @click.stop>
<div class="flex items-center justify-between p-5 border-b border-dark-500">
<h3 class="text-lg font-semibold">Add MCP Server</h3>
<button @click="closeAddMcp" class="btn-icon">
<X class="w-5 h-5" />
</button>
</div>
<div class="p-5 space-y-4 max-h-[70vh] overflow-y-auto">
<!-- MCP Name -->
<div>
<label class="block text-sm font-medium text-gray-300 mb-2">MCP Name <span class="text-red-400">*</span></label>
<input v-model="mcpForm.name" type="text" class="input-field" placeholder="e.g., filesystem, memory">
</div>
<!-- Description -->
<div>
<label class="block text-sm font-medium text-gray-300 mb-2">Description (English)</label>
<textarea v-model="mcpForm.description" rows="2" class="input-field resize-none" placeholder="Describe what this MCP server does"></textarea>
</div>
<!-- Description CN -->
<div>
<label class="block text-sm font-medium text-gray-300 mb-2">Description (中文)</label>
<textarea v-model="mcpForm.description_cn" rows="2" class="input-field resize-none" placeholder="描述这个 MCP 服务器的功能"></textarea>
</div>
<!-- Transport -->
<div>
<label class="block text-sm font-medium text-gray-300 mb-2">Transport Protocol <span class="text-red-400">*</span></label>
<select v-model="mcpForm.transport" class="input-field">
<option value="stdio">stdio</option>
<option value="http">HTTP</option>
<option value="sse">SSE</option>
</select>
</div>
<!-- Command -->
<div>
<label class="block text-sm font-medium text-gray-300 mb-2">Start Command <span class="text-red-400">*</span></label>
<input v-model="mcpForm.command" type="text" class="input-field" placeholder="e.g., npx, python, node">
</div>
<!-- Args -->
<div>
<label class="block text-sm font-medium text-gray-300 mb-2">Arguments</label>
<textarea v-model="mcpForm.args" rows="3" class="input-field resize-none" placeholder="One argument per line, e.g.:&#10;-y&#10;@modelcontextprotocol/server-filesystem&#10;/path/to/dir"></textarea>
</div>
<!-- Environment Variables -->
<div>
<label class="block text-sm font-medium text-gray-300 mb-2">Environment Variables</label>
<textarea v-model="mcpForm.env" rows="3" class="input-field resize-none" placeholder="KEY=value format, one per line, e.g.:&#10;API_KEY=xxx&#10;DEBUG=true"></textarea>
</div>
</div>
<div class="flex items-center justify-end gap-3 p-5 border-t border-dark-500">
<button @click="closeAddMcp" class="btn-secondary">Cancel</button>
<button @click="submitMcp" class="btn-primary" :disabled="!mcpForm.name || !mcpForm.command">
Add MCP
</button>
</div>
</div>
</div>
</Teleport>
</div>
</template>

View File

@@ -1,19 +1,49 @@
import { ref, computed } from 'vue'
import { ElMessageBox } from 'element-plus'
import { ref, computed, onMounted } from 'vue'
import { ElMessageBox, ElMessage } from 'element-plus'
const API_BASE = 'http://localhost:8082'
export interface Skill {
id: number
id: string
name: string
description: string
type: string
description_cn?: string
command: string
args?: string
env?: string
category: string
status: 'running' | 'stopped' | 'error'
port: number
createdAt: string
tools: number
status: string
created_at?: string
}
export function useSkills() {
// 从 API 获取 MCP 列表
const fetchSkills = async () => {
try {
const response = await fetch(`${API_BASE}/mcp/list`)
const result = await response.json()
if (result && Array.isArray(result)) {
skills.value = result.map((mcp: any) => ({
id: mcp.id,
name: mcp.name,
description: mcp.description || '',
description_cn: mcp.description_cn || '',
command: mcp.command || '',
args: mcp.args || '',
env: mcp.env || '',
category: mcp.category || 'custom',
status: mcp.status || 'stopped',
created_at: mcp.created_at,
}))
}
} catch (error) {
console.error('Failed to fetch MCP list:', error)
}
}
onMounted(() => {
fetchSkills()
})
// Skill 数据
const skills = ref<Skill[]>([
{ id: 1, name: 'Linear', description: 'Linear API integration for project management', type: 'API', category: 'api', status: 'running', port: 3001, createdAt: '2025-04-10', tools: 12 },