| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287 |
- package mcpTool
- import (
- "context"
- "encoding/json"
- "errors"
- "fmt"
- "github.com/flipped-aurora/gin-vue-admin/server/global"
- "github.com/flipped-aurora/gin-vue-admin/server/model/system"
- "github.com/flipped-aurora/gin-vue-admin/server/service"
- "github.com/mark3labs/mcp-go/mcp"
- "go.uber.org/zap"
- )
- // 注册工具
- func init() {
- RegisterTool(&MenuCreator{})
- }
- // MenuCreateRequest 菜单创建请求结构
- type MenuCreateRequest struct {
- ParentId uint `json:"parentId"` // 父菜单ID,0表示根菜单
- Path string `json:"path"` // 路由path
- Name string `json:"name"` // 路由name
- Hidden bool `json:"hidden"` // 是否在列表隐藏
- Component string `json:"component"` // 对应前端文件路径
- Sort int `json:"sort"` // 排序标记
- Title string `json:"title"` // 菜单名
- Icon string `json:"icon"` // 菜单图标
- KeepAlive bool `json:"keepAlive"` // 是否缓存
- DefaultMenu bool `json:"defaultMenu"` // 是否是基础路由
- CloseTab bool `json:"closeTab"` // 自动关闭tab
- ActiveName string `json:"activeName"` // 高亮菜单
- Parameters []MenuParameterRequest `json:"parameters"` // 路由参数
- MenuBtn []MenuButtonRequest `json:"menuBtn"` // 菜单按钮
- }
- // MenuParameterRequest 菜单参数请求结构
- type MenuParameterRequest struct {
- Type string `json:"type"` // 参数类型:params或query
- Key string `json:"key"` // 参数key
- Value string `json:"value"` // 参数值
- }
- // MenuButtonRequest 菜单按钮请求结构
- type MenuButtonRequest struct {
- Name string `json:"name"` // 按钮名称
- Desc string `json:"desc"` // 按钮描述
- }
- // MenuCreateResponse 菜单创建响应结构
- type MenuCreateResponse struct {
- Success bool `json:"success"`
- Message string `json:"message"`
- MenuID uint `json:"menuId"`
- Name string `json:"name"`
- Path string `json:"path"`
- }
- // MenuCreator 菜单创建工具
- type MenuCreator struct{}
- // New 创建菜单创建工具
- func (m *MenuCreator) New() mcp.Tool {
- return mcp.NewTool("create_menu",
- mcp.WithDescription(`创建前端菜单记录,用于AI编辑器自动添加前端页面时自动创建对应的菜单项。
- **重要限制:**
- - 当使用gva_auto_generate工具且needCreatedModules=true时,模块创建会自动生成菜单项,不应调用此工具
- - 仅在以下情况使用:1) 单独创建菜单(不涉及模块创建);2) AI编辑器自动添加前端页面时`),
- mcp.WithNumber("parentId",
- mcp.Description("父菜单ID,0表示根菜单"),
- mcp.DefaultNumber(0),
- ),
- mcp.WithString("path",
- mcp.Required(),
- mcp.Description("路由path,如:userList"),
- ),
- mcp.WithString("name",
- mcp.Required(),
- mcp.Description("路由name,用于Vue Router,如:userList"),
- ),
- mcp.WithBoolean("hidden",
- mcp.Description("是否在菜单列表中隐藏"),
- ),
- mcp.WithString("component",
- mcp.Required(),
- mcp.Description("对应的前端Vue组件路径,如:view/user/list.vue"),
- ),
- mcp.WithNumber("sort",
- mcp.Description("菜单排序号,数字越小越靠前"),
- mcp.DefaultNumber(1),
- ),
- mcp.WithString("title",
- mcp.Required(),
- mcp.Description("菜单显示标题"),
- ),
- mcp.WithString("icon",
- mcp.Description("菜单图标名称"),
- mcp.DefaultString("menu"),
- ),
- mcp.WithBoolean("keepAlive",
- mcp.Description("是否缓存页面"),
- ),
- mcp.WithBoolean("defaultMenu",
- mcp.Description("是否是基础路由"),
- ),
- mcp.WithBoolean("closeTab",
- mcp.Description("是否自动关闭tab"),
- ),
- mcp.WithString("activeName",
- mcp.Description("高亮菜单名称"),
- ),
- mcp.WithString("parameters",
- mcp.Description("路由参数JSON字符串,格式:[{\"type\":\"params\",\"key\":\"id\",\"value\":\"1\"}]"),
- ),
- mcp.WithString("menuBtn",
- mcp.Description("菜单按钮JSON字符串,格式:[{\"name\":\"add\",\"desc\":\"新增\"}]"),
- ),
- )
- }
- // Handle 处理菜单创建请求
- func (m *MenuCreator) Handle(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
- // 解析请求参数
- args := request.GetArguments()
- // 必需参数
- path, ok := args["path"].(string)
- if !ok || path == "" {
- return nil, errors.New("path 参数是必需的")
- }
- name, ok := args["name"].(string)
- if !ok || name == "" {
- return nil, errors.New("name 参数是必需的")
- }
- component, ok := args["component"].(string)
- if !ok || component == "" {
- return nil, errors.New("component 参数是必需的")
- }
- title, ok := args["title"].(string)
- if !ok || title == "" {
- return nil, errors.New("title 参数是必需的")
- }
- // 可选参数
- parentId := uint(0)
- if val, ok := args["parentId"].(float64); ok {
- parentId = uint(val)
- }
- hidden := false
- if val, ok := args["hidden"].(bool); ok {
- hidden = val
- }
- sort := 1
- if val, ok := args["sort"].(float64); ok {
- sort = int(val)
- }
- icon := "menu"
- if val, ok := args["icon"].(string); ok && val != "" {
- icon = val
- }
- keepAlive := false
- if val, ok := args["keepAlive"].(bool); ok {
- keepAlive = val
- }
- defaultMenu := false
- if val, ok := args["defaultMenu"].(bool); ok {
- defaultMenu = val
- }
- closeTab := false
- if val, ok := args["closeTab"].(bool); ok {
- closeTab = val
- }
- activeName := ""
- if val, ok := args["activeName"].(string); ok {
- activeName = val
- }
- // 解析参数和按钮
- var parameters []system.SysBaseMenuParameter
- if parametersStr, ok := args["parameters"].(string); ok && parametersStr != "" {
- var paramReqs []MenuParameterRequest
- if err := json.Unmarshal([]byte(parametersStr), ¶mReqs); err != nil {
- return nil, fmt.Errorf("parameters 参数格式错误: %v", err)
- }
- for _, param := range paramReqs {
- parameters = append(parameters, system.SysBaseMenuParameter{
- Type: param.Type,
- Key: param.Key,
- Value: param.Value,
- })
- }
- }
- var menuBtn []system.SysBaseMenuBtn
- if menuBtnStr, ok := args["menuBtn"].(string); ok && menuBtnStr != "" {
- var btnReqs []MenuButtonRequest
- if err := json.Unmarshal([]byte(menuBtnStr), &btnReqs); err != nil {
- return nil, fmt.Errorf("menuBtn 参数格式错误: %v", err)
- }
- for _, btn := range btnReqs {
- menuBtn = append(menuBtn, system.SysBaseMenuBtn{
- Name: btn.Name,
- Desc: btn.Desc,
- })
- }
- }
- // 构建菜单对象
- menu := system.SysBaseMenu{
- ParentId: parentId,
- Path: path,
- Name: name,
- Hidden: hidden,
- Component: component,
- Sort: sort,
- Meta: system.Meta{
- Title: title,
- Icon: icon,
- KeepAlive: keepAlive,
- DefaultMenu: defaultMenu,
- CloseTab: closeTab,
- ActiveName: activeName,
- },
- Parameters: parameters,
- MenuBtn: menuBtn,
- }
- // 创建菜单
- menuService := service.ServiceGroupApp.SystemServiceGroup.MenuService
- err := menuService.AddBaseMenu(menu)
- if err != nil {
- return nil, fmt.Errorf("创建菜单失败: %v", err)
- }
- // 获取创建的菜单ID
- var createdMenu system.SysBaseMenu
- err = global.GVA_DB.Where("name = ? AND path = ?", name, path).First(&createdMenu).Error
- if err != nil {
- global.GVA_LOG.Warn("获取创建的菜单ID失败", zap.Error(err))
- }
- // 构建响应
- response := &MenuCreateResponse{
- Success: true,
- Message: fmt.Sprintf("成功创建菜单 %s", title),
- MenuID: createdMenu.ID,
- Name: name,
- Path: path,
- }
- resultJSON, err := json.MarshalIndent(response, "", " ")
- if err != nil {
- return nil, fmt.Errorf("序列化结果失败: %v", err)
- }
- // 添加权限分配提醒
- permissionReminder := "\n\n⚠️ 重要提醒:\n" +
- "菜单创建完成后,请前往【系统管理】->【角色管理】中为相关角色分配新创建的菜单权限," +
- "以确保用户能够正常访问新菜单。\n" +
- "具体步骤:\n" +
- "1. 进入角色管理页面\n" +
- "2. 选择需要授权的角色\n" +
- "3. 在菜单权限中勾选新创建的菜单项\n" +
- "4. 保存权限配置"
- return &mcp.CallToolResult{
- Content: []mcp.Content{
- mcp.TextContent{
- Type: "text",
- Text: fmt.Sprintf("菜单创建结果:\n\n%s%s", string(resultJSON), permissionReminder),
- },
- },
- }, nil
- }
|