diff --git a/internal/controllers/console/mcp_controller.go b/internal/controllers/console/mcp_controller.go index 25ea237..fee85df 100644 --- a/internal/controllers/console/mcp_controller.go +++ b/internal/controllers/console/mcp_controller.go @@ -24,6 +24,29 @@ func (c *MCPController) AnyList_servers() *web.JsonResult { return web.JsonData(response.BuildMCPServerInfoResponses(services.MCPDebugService.ListServers())) } +func (c *MCPController) AnyCatalog() *web.JsonResult { + if _, err := services.AuthService.RequirePermission(c.Ctx, constants.PermissionMCPView); err != nil { + return web.JsonError(err) + } + items, err := services.ToolCatalogService.ListMCPTools(context.Background()) + if err != nil { + return web.JsonError(err) + } + ret := make([]response.MCPToolCatalogResponse, 0, len(items)) + for _, item := range items { + ret = append(ret, response.MCPToolCatalogResponse{ + ToolCode: item.ToolCode, + ServerCode: item.ServerCode, + ToolName: item.ToolName, + Title: item.Title, + Description: item.Description, + InputSchema: item.InputSchema, + OutputSchema: item.OutputSchema, + }) + } + return web.JsonData(ret) +} + func (c *MCPController) PostTest_connection() *web.JsonResult { if _, err := services.AuthService.RequirePermission(c.Ctx, constants.PermissionMCPView); err != nil { return web.JsonError(err) diff --git a/internal/controllers/console/skill_definition_controller.go b/internal/controllers/console/skill_definition_controller.go index 6bd56d2..9ab5e56 100644 --- a/internal/controllers/console/skill_definition_controller.go +++ b/internal/controllers/console/skill_definition_controller.go @@ -257,9 +257,15 @@ func validateSkillDefinitionRequest(req request.CreateSkillDefinitionRequest) er if _, err := normalizeJSONStringArray(req.Examples); err != nil { return err } - if _, err := normalizeJSONStringArray(req.AllowedToolCodes); err != nil { + allowedToolCodes, err := normalizeJSONStringArray(req.AllowedToolCodes) + if err != nil { return err } + for _, toolCode := range allowedToolCodes { + if err := services.ToolCatalogService.ValidateMCPToolCode(toolCode); err != nil { + return err + } + } return nil } diff --git a/internal/pkg/dto/response/mcp_response.go b/internal/pkg/dto/response/mcp_response.go index 7713ff8..174a0c0 100644 --- a/internal/pkg/dto/response/mcp_response.go +++ b/internal/pkg/dto/response/mcp_response.go @@ -65,6 +65,16 @@ func BuildMCPToolInfoResponses(items []mcps.ToolInfo) []MCPToolInfoResponse { return ret } +type MCPToolCatalogResponse struct { + ToolCode string `json:"toolCode"` + ServerCode string `json:"serverCode"` + ToolName string `json:"toolName"` + Title string `json:"title"` + Description string `json:"description"` + InputSchema any `json:"inputSchema"` + OutputSchema any `json:"outputSchema,omitempty"` +} + type MCPToolResultContentResponse struct { Type string `json:"type"` Text string `json:"text,omitempty"` diff --git a/internal/services/tool_catalog_service.go b/internal/services/tool_catalog_service.go new file mode 100644 index 0000000..e010ca4 --- /dev/null +++ b/internal/services/tool_catalog_service.go @@ -0,0 +1,83 @@ +package services + +import ( + "context" + "slices" + "strings" + + "cs-agent/internal/ai/mcps" + "cs-agent/internal/pkg/config" + "cs-agent/internal/pkg/errorsx" + "cs-agent/internal/pkg/toolx" +) + +var ToolCatalogService = newToolCatalogService() + +func newToolCatalogService() *toolCatalogService { + return &toolCatalogService{} +} + +type toolCatalogService struct{} + +type MCPToolCatalogItem struct { + ToolCode string + ServerCode string + ToolName string + Title string + Description string + InputSchema any + OutputSchema any +} + +func (s *toolCatalogService) ListMCPTools(ctx context.Context) ([]MCPToolCatalogItem, error) { + cfg := config.Current() + if !cfg.MCP.Enabled { + return nil, errorsx.InvalidParam("MCP未启用") + } + if len(cfg.MCP.Servers) == 0 { + return nil, nil + } + serverCodes := make([]string, 0, len(cfg.MCP.Servers)) + for serverCode, server := range cfg.MCP.Servers { + if !server.Enabled { + continue + } + serverCodes = append(serverCodes, serverCode) + } + slices.Sort(serverCodes) + ret := make([]MCPToolCatalogItem, 0) + for _, serverCode := range serverCodes { + tools, err := mcps.Runtime.ListTools(ctx, serverCode) + if err != nil { + return nil, err + } + for _, item := range tools { + ret = append(ret, MCPToolCatalogItem{ + ToolCode: toolx.BuildMCPToolCode(serverCode, item.Name), + ServerCode: serverCode, + ToolName: strings.TrimSpace(item.Name), + Title: strings.TrimSpace(item.Title), + Description: strings.TrimSpace(item.Description), + InputSchema: item.InputSchema, + OutputSchema: item.OutputSchema, + }) + } + } + return ret, nil +} + +func (s *toolCatalogService) ValidateMCPToolCode(toolCode string) error { + cfg := config.Current() + if !cfg.MCP.Enabled { + return errorsx.InvalidParam("MCP未启用") + } + serverCode, toolName := toolx.SplitMCPToolCode(toolCode) + if serverCode == "" || toolName == "" { + return errorsx.InvalidParam("toolCode格式不合法") + } + server, ok := cfg.MCP.Servers[serverCode] + if !ok || !server.Enabled { + return errorsx.InvalidParam("toolCode 绑定的 MCP 服务不存在或未启用") + } + return nil +}