From cab6488d71cc5ab3298199e60c341580d1ac8271 Mon Sep 17 00:00:00 2001 From: "DESKTOP-72TV0V4\\caoxiaozhu" Date: Wed, 11 Mar 2026 14:45:42 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9E=20MCP=20=E6=A8=A1?= =?UTF-8?q?=E5=9D=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - mcp_handler.go, mcp.go, mcp_repo.go, mcp_service.go Co-Authored-By: Claude Opus 4.6 --- server/internal/handler/mcp_handler.go | 154 +++++++++++++++++++++++++ server/internal/model/mcp.go | 32 +++++ server/internal/repository/mcp_repo.go | 67 +++++++++++ server/internal/service/mcp_service.go | 42 +++++++ 4 files changed, 295 insertions(+) create mode 100644 server/internal/handler/mcp_handler.go create mode 100644 server/internal/model/mcp.go create mode 100644 server/internal/repository/mcp_repo.go create mode 100644 server/internal/service/mcp_service.go diff --git a/server/internal/handler/mcp_handler.go b/server/internal/handler/mcp_handler.go new file mode 100644 index 0000000..e8fb9d8 --- /dev/null +++ b/server/internal/handler/mcp_handler.go @@ -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"}) +} diff --git a/server/internal/model/mcp.go b/server/internal/model/mcp.go new file mode 100644 index 0000000..351a2f4 --- /dev/null +++ b/server/internal/model/mcp.go @@ -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 +} diff --git a/server/internal/repository/mcp_repo.go b/server/internal/repository/mcp_repo.go new file mode 100644 index 0000000..fba0fa2 --- /dev/null +++ b/server/internal/repository/mcp_repo.go @@ -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 +} diff --git a/server/internal/service/mcp_service.go b/server/internal/service/mcp_service.go new file mode 100644 index 0000000..0650114 --- /dev/null +++ b/server/internal/service/mcp_service.go @@ -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) +}