Files
Go-Web-Template/server/mcp/menu_creator.go
2026-04-10 20:41:28 +08:00

229 lines
6.5 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package mcpTool
import (
"context"
"encoding/json"
"errors"
"fmt"
"git.echol.cn/loser/Go-Web-Template/server/model/system"
"github.com/mark3labs/mcp-go/mcp"
)
func init() {
RegisterTool(&MenuCreator{})
}
type MenuCreateRequest struct {
ParentId uint `json:"parentId"`
Path string `json:"path"`
Name string `json:"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"`
ActiveName string `json:"activeName"`
Parameters []MenuParameterRequest `json:"parameters"`
MenuBtn []MenuButtonRequest `json:"menuBtn"`
}
type MenuParameterRequest struct {
Type string `json:"type"`
Key string `json:"key"`
Value string `json:"value"`
}
type MenuButtonRequest struct {
Name string `json:"name"`
Desc string `json:"desc"`
}
type MenuCreateResponse struct {
Success bool `json:"success"`
Message string `json:"message"`
MenuID uint `json:"menuId"`
Name string `json:"name"`
Path string `json:"path"`
}
type MenuCreator struct{}
func (m *MenuCreator) New() mcp.Tool {
return mcp.NewTool("create_menu",
mcp.WithDescription(`创建前端菜单记录用于AI编辑器自动添加前端页面时自动创建对应的菜单项。
**重要限制:**
- 当使用模块自动生成功能且 needCreatedModules=true 时,模块创建会自动生成菜单项,不应调用此工具
- 仅在以下情况使用1) 单独创建菜单不涉及模块创建2) AI编辑器自动添加前端页面时`),
mcp.WithNumber("parentId",
mcp.Description("父菜单ID0表示根菜单"),
mcp.DefaultNumber(0),
),
mcp.WithString("path",
mcp.Required(),
mcp.Description("路由pathuserList"),
),
mcp.WithString("name",
mcp.Required(),
mcp.Description("路由name用于前端路由标识userList"),
),
mcp.WithBoolean("hidden",
mcp.Description("是否在菜单列表中隐藏"),
),
mcp.WithString("component",
mcp.Required(),
mcp.Description("对应的前端React组件路径features/users/UserManagementPage"),
),
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\":\"新增\"}]"),
),
)
}
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 value, ok := args["parentId"].(float64); ok {
parentID = uint(value)
}
hidden, _ := args["hidden"].(bool)
sort := 1
if value, ok := args["sort"].(float64); ok {
sort = int(value)
}
icon := "menu"
if value, ok := args["icon"].(string); ok && value != "" {
icon = value
}
keepAlive, _ := args["keepAlive"].(bool)
defaultMenu, _ := args["defaultMenu"].(bool)
closeTab, _ := args["closeTab"].(bool)
activeName, _ := args["activeName"].(string)
parameters := make([]system.SysBaseMenuParameter, 0)
if parametersStr, ok := args["parameters"].(string); ok && parametersStr != "" {
var paramReqs []MenuParameterRequest
if err := json.Unmarshal([]byte(parametersStr), &paramReqs); 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,
})
}
}
menuBtns := make([]system.SysBaseMenuBtn, 0)
if menuBtnStr, ok := args["menuBtn"].(string); ok && menuBtnStr != "" {
var buttonReqs []MenuButtonRequest
if err := json.Unmarshal([]byte(menuBtnStr), &buttonReqs); err != nil {
return nil, fmt.Errorf("menuBtn 参数格式错误: %v", err)
}
for _, button := range buttonReqs {
menuBtns = append(menuBtns, system.SysBaseMenuBtn{
Name: button.Name,
Desc: button.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: menuBtns,
}
if _, err := postUpstream[map[string]any](ctx, "/menu/addBaseMenu", menu); err != nil {
return nil, fmt.Errorf("创建菜单失败: %v", err)
}
menuID := uint(0)
if menuListResp, err := postUpstream[[]system.SysBaseMenu](ctx, "/menu/getMenuList", map[string]any{}); err == nil {
menuID = findMenuID(menuListResp.Data, name, path)
}
return textResultWithJSON("菜单创建结果:", &MenuCreateResponse{
Success: true,
Message: fmt.Sprintf("成功创建菜单 %s", title),
MenuID: menuID,
Name: name,
Path: path,
})
}
func findMenuID(menus []system.SysBaseMenu, name, path string) uint {
for _, menu := range menus {
if menu.Name == name && menu.Path == path {
return menu.ID
}
if id := findMenuID(menu.Children, name, path); id != 0 {
return id
}
}
return 0
}