diff --git a/server/api/v1/system/auto_code_mcp.go b/server/api/v1/system/auto_code_mcp.go index 14dc3157..c3f6188b 100644 --- a/server/api/v1/system/auto_code_mcp.go +++ b/server/api/v1/system/auto_code_mcp.go @@ -1,10 +1,13 @@ package system import ( + "fmt" "github.com/flipped-aurora/gin-vue-admin/server/global" + "github.com/flipped-aurora/gin-vue-admin/server/mcp/client" "github.com/flipped-aurora/gin-vue-admin/server/model/common/response" "github.com/flipped-aurora/gin-vue-admin/server/model/system/request" "github.com/gin-gonic/gin" + "github.com/mark3labs/mcp-go/mcp" ) // Create @@ -32,3 +35,99 @@ func (a *AutoCodeTemplateApi) MCP(c *gin.Context) { } response.OkWithMessage("创建成功,MCP Tool路径:"+toolFilePath, c) } + +// Create +// @Tags mcp +// @Summary 自动McpTool +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body request.AutoMcpTool true "创建自动代码" +// @Success 200 {string} string "{"success":true,"data":{},"msg":"创建成功"}" +// @Router /autoCode/mcpList [post] +func (a *AutoCodeTemplateApi) MCPList(c *gin.Context) { + + baseUrl := fmt.Sprintf("http://127.0.0.1:%d%s", global.GVA_CONFIG.System.Addr, global.GVA_CONFIG.MCP.SSEPath) + + testClient, err := client.NewClient(baseUrl, "testClient", "v1.0.0", global.GVA_CONFIG.MCP.Name) + defer testClient.Close() + toolsRequest := mcp.ListToolsRequest{} + + list, err := testClient.ListTools(c.Request.Context(), toolsRequest) + + if err != nil { + response.FailWithMessage("创建失败", c) + global.GVA_LOG.Error(err.Error()) + return + } + response.OkWithData(list, c) +} + +// Create +// @Tags mcp +// @Summary 测试McpTool +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body object true "调用MCP Tool的参数" +// @Success 200 {object} response.Response "{"success":true,"data":{},"msg":"测试成功"}" +// @Router /autoCode/mcpTest [post] +func (a *AutoCodeTemplateApi) MCPTest(c *gin.Context) { + // 定义接口请求结构 + var testRequest struct { + Name string `json:"name" binding:"required"` // 工具名称 + Arguments map[string]interface{} `json:"arguments" binding:"required"` // 工具参数 + } + + // 绑定JSON请求体 + if err := c.ShouldBindJSON(&testRequest); err != nil { + response.FailWithMessage("参数解析失败:"+err.Error(), c) + return + } + + // 创建MCP客户端 + baseUrl := fmt.Sprintf("http://127.0.0.1:%d%s", global.GVA_CONFIG.System.Addr, global.GVA_CONFIG.MCP.SSEPath) + testClient, err := client.NewClient(baseUrl, "testClient", "v1.0.0", global.GVA_CONFIG.MCP.Name) + if err != nil { + response.FailWithMessage("创建MCP客户端失败:"+err.Error(), c) + return + } + defer testClient.Close() + + ctx := c.Request.Context() + + // 初始化MCP连接 + initRequest := mcp.InitializeRequest{} + initRequest.Params.ProtocolVersion = mcp.LATEST_PROTOCOL_VERSION + initRequest.Params.ClientInfo = mcp.Implementation{ + Name: "testClient", + Version: "v1.0.0", + } + + _, err = testClient.Initialize(ctx, initRequest) + if err != nil { + response.FailWithMessage("初始化MCP连接失败:"+err.Error(), c) + return + } + + // 构建工具调用请求 + request := mcp.CallToolRequest{} + request.Params.Name = testRequest.Name + request.Params.Arguments = testRequest.Arguments + + // 调用工具 + result, err := testClient.CallTool(ctx, request) + if err != nil { + response.FailWithMessage("工具调用失败:"+err.Error(), c) + return + } + + // 处理响应结果 + if len(result.Content) == 0 { + response.FailWithMessage("工具未返回任何内容", c) + return + } + + // 返回结果 + response.OkWithData(result.Content, c) +} diff --git a/server/mcp/current_time.go b/server/mcp/current_time.go index d6f0fc7a..72dc74e9 100644 --- a/server/mcp/current_time.go +++ b/server/mcp/current_time.go @@ -2,6 +2,8 @@ package mcpTool import ( "context" + "errors" + "fmt" "github.com/mark3labs/mcp-go/mcp" "time" ) @@ -16,13 +18,26 @@ type CurrentTime struct { // 获取当前系统时间 func (t *CurrentTime) Handle(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { // 获取当前系统时间 - currentTime := time.Now().Format("2006-01-02 15:04:05") + + timezone, ok := request.Params.Arguments["timezone"].(string) + + if !ok { + return nil, errors.New("参数错误:timezone 必须是字符串类型") + } + // 根据timezone参数加载对应时区 + loc, err := loadTimeZone(timezone) + if err != nil { + return nil, err + } + + // 获取当前时间并转换为指定时区 + currentTime := time.Now().In(loc).Format("2006-01-02 15:04:05") //返回 return &mcp.CallToolResult{ Content: []mcp.Content{ mcp.TextContent{ Type: "text", - Text: currentTime, + Text: fmt.Sprintf("%s 时区的当前时间是:%s", timezone, currentTime), }, }, }, nil @@ -37,3 +52,30 @@ func (t *CurrentTime) New() mcp.Tool { mcp.Enum("UTC", "CST", "PST", "EST", "GMT", "CET", "JST", "MST", "IST", "AST", "HST"), )) } + +// 将简写时区转换为IANA标准时区 +func loadTimeZone(timezone string) (*time.Location, error) { + // 时区映射表 + timezoneMap := map[string]string{ + "UTC": "UTC", + "CST": "Asia/Shanghai", // 中国标准时间 + "PST": "America/Los_Angeles", + "EST": "America/New_York", + "GMT": "GMT", + "CET": "Europe/Paris", + "JST": "Asia/Tokyo", + "MST": "America/Denver", + "IST": "Asia/Kolkata", + "AST": "Asia/Riyadh", // 阿拉伯标准时间 + "HST": "Pacific/Honolulu", + } + + // 获取标准时区名称 + tzName, exists := timezoneMap[timezone] + if !exists { + return nil, errors.New("不支持的时区: " + timezone) + } + + // 加载时区 + return time.LoadLocation(tzName) +} diff --git a/server/mcp/get_nickname.go b/server/mcp/get_nickname.go index 02c232c7..b1af73e1 100644 --- a/server/mcp/get_nickname.go +++ b/server/mcp/get_nickname.go @@ -31,15 +31,15 @@ func (t *GetNickname) Handle(ctx context.Context, request mcp.CallToolRequest) ( // 1. 参数验证 username, ok := request.Params.Arguments["username"].(string) if !ok { - return nil, errors.New("参数错误:username必须是字符串类型") + return nil, errors.New("参数错误:username 必须是字符串类型") } if username == "" { - return nil, errors.New("参数错误:username不能为空") + return nil, errors.New("参数错误:username 不能为空") } // 2. 记录操作日志 - global.GVA_LOG.Info("getNickname工具被调用") + global.GVA_LOG.Info("getNickname 工具被调用") // 3. 优化查询,只选择需要的字段 var user struct { @@ -54,7 +54,14 @@ func (t *GetNickname) Handle(ctx context.Context, request mcp.CallToolRequest) ( // 4. 优化错误处理 if err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { - return nil, errors.New("用户不存在") + return &mcp.CallToolResult{ + Content: []mcp.Content{ + mcp.TextContent{ + Type: "text", + Text: fmt.Sprintf("用户 %s 不存在", username), + }, + }, + }, nil } global.GVA_LOG.Error("数据库查询错误") return nil, errors.New("系统错误,请稍后再试") diff --git a/server/router/system/sys_auto_code.go b/server/router/system/sys_auto_code.go index 68fb8494..ef89245c 100644 --- a/server/router/system/sys_auto_code.go +++ b/server/router/system/sys_auto_code.go @@ -20,7 +20,9 @@ func (s *AutoCodeRouter) InitAutoCodeRouter(Router *gin.RouterGroup, RouterPubli autoCodeRouter.POST("addFunc", autoCodeTemplateApi.AddFunc) // 为代码插入方法 } { - autoCodeRouter.POST("mcp", autoCodeTemplateApi.MCP) // 自动创建Mcp Tool模板 + autoCodeRouter.POST("mcp", autoCodeTemplateApi.MCP) // 自动创建Mcp Tool模板 + autoCodeRouter.POST("mcpList", autoCodeTemplateApi.MCPList) // 获取MCP ToolList + autoCodeRouter.POST("mcpTest", autoCodeTemplateApi.MCPTest) // MCP 工具测试 } { autoCodeRouter.POST("getPackage", autoCodePackageApi.All) // 获取package包 diff --git a/server/source/system/api.go b/server/source/system/api.go index a0265186..c28b3733 100644 --- a/server/source/system/api.go +++ b/server/source/system/api.go @@ -118,6 +118,8 @@ func (i *initApi) InitializeData(ctx context.Context) (context.Context, error) { {ApiGroup: "代码生成器", Method: "POST", Path: "/autoCode/installPlugin", Description: "安装插件"}, {ApiGroup: "代码生成器", Method: "POST", Path: "/autoCode/pubPlug", Description: "打包插件"}, {ApiGroup: "代码生成器", Method: "POST", Path: "/autoCode/mcp", Description: "自动生成 MCP Tool 模板"}, + {ApiGroup: "代码生成器", Method: "POST", Path: "/autoCode/mcpTest", Description: "MCP Tool 测试"}, + {ApiGroup: "代码生成器", Method: "POST", Path: "/autoCode/mcpList", Description: "获取 MCP ToolList"}, {ApiGroup: "模板配置", Method: "POST", Path: "/autoCode/createPackage", Description: "配置模板"}, {ApiGroup: "模板配置", Method: "GET", Path: "/autoCode/getTemplates", Description: "获取模板文件"}, diff --git a/server/source/system/casbin.go b/server/source/system/casbin.go index e9da7551..4e37c147 100644 --- a/server/source/system/casbin.go +++ b/server/source/system/casbin.go @@ -131,6 +131,8 @@ func (i *initCasbin) InitializeData(ctx context.Context) (context.Context, error {Ptype: "p", V0: "888", V1: "/autoCode/pubPlug", V2: "POST"}, {Ptype: "p", V0: "888", V1: "/autoCode/addFunc", V2: "POST"}, {Ptype: "p", V0: "888", V1: "/autoCode/mcp", V2: "POST"}, + {Ptype: "p", V0: "888", V1: "/autoCode/mcpTest", V2: "POST"}, + {Ptype: "p", V0: "888", V1: "/autoCode/mcpList", V2: "POST"}, {Ptype: "p", V0: "888", V1: "/sysDictionaryDetail/findSysDictionaryDetail", V2: "GET"}, {Ptype: "p", V0: "888", V1: "/sysDictionaryDetail/updateSysDictionaryDetail", V2: "PUT"}, diff --git a/server/source/system/menu.go b/server/source/system/menu.go index ba64f6a0..0fbd1dbb 100644 --- a/server/source/system/menu.go +++ b/server/source/system/menu.go @@ -101,6 +101,7 @@ func (i *initMenu) InitializeData(ctx context.Context) (next context.Context, er {MenuLevel: 1, Hidden: false, ParentId: menuNameMap["systemTools"], Path: "exportTemplate", Name: "exportTemplate", Component: "view/systemTools/exportTemplate/exportTemplate.vue", Sort: 5, Meta: Meta{Title: "导出模板", Icon: "reading"}}, {MenuLevel: 1, Hidden: false, ParentId: menuNameMap["systemTools"], Path: "picture", Name: "picture", Component: "view/systemTools/autoCode/picture.vue", Sort: 6, Meta: Meta{Title: "AI页面绘制", Icon: "picture-filled"}}, {MenuLevel: 1, Hidden: false, ParentId: menuNameMap["systemTools"], Path: "mcpTool", Name: "mcpTool", Component: "view/systemTools/autoCode/mcp.vue", Sort: 7, Meta: Meta{Title: "Mcp Tools模板", Icon: "magnet"}}, + {MenuLevel: 1, Hidden: false, ParentId: menuNameMap["systemTools"], Path: "mcpTest", Name: "mcpTest", Component: "view/systemTools/autoCode/mcpTest.vue", Sort: 7, Meta: Meta{Title: "Mcp Tools测试", Icon: "partly-cloudy"}}, {MenuLevel: 1, Hidden: false, ParentId: menuNameMap["plugin"], Path: "https://plugin.gin-vue-admin.com/", Name: "https://plugin.gin-vue-admin.com/", Component: "https://plugin.gin-vue-admin.com/", Sort: 0, Meta: Meta{Title: "插件市场", Icon: "shop"}}, {MenuLevel: 1, Hidden: false, ParentId: menuNameMap["plugin"], Path: "installPlugin", Name: "installPlugin", Component: "view/systemTools/installPlugin/index.vue", Sort: 1, Meta: Meta{Title: "插件安装", Icon: "box"}}, diff --git a/web/src/api/autoCode.js b/web/src/api/autoCode.js index a6a1118b..cb3443f1 100644 --- a/web/src/api/autoCode.js +++ b/web/src/api/autoCode.js @@ -215,3 +215,21 @@ export const mcp = (data) => { data }) } + + +export const mcpList = (data) => { + return service({ + url: '/autoCode/mcpList', + method: 'post', + data + }) +} + + +export const mcpTest = (data) => { + return service({ + url: '/autoCode/mcpTest', + method: 'post', + data + }) +} diff --git a/web/src/pathInfo.json b/web/src/pathInfo.json index 76707c42..e7c4f18f 100644 --- a/web/src/pathInfo.json +++ b/web/src/pathInfo.json @@ -58,6 +58,7 @@ "/src/view/systemTools/autoCode/component/previewCodeDialog.vue": "PreviewCodeDialog", "/src/view/systemTools/autoCode/index.vue": "AutoCode", "/src/view/systemTools/autoCode/mcp.vue": "Mcp", + "/src/view/systemTools/autoCode/mcpToolsTest.vue": "McpToolsTest", "/src/view/systemTools/autoCode/picture.vue": "Picture", "/src/view/systemTools/autoCodeAdmin/index.vue": "AutoCodeAdmin", "/src/view/systemTools/autoPkg/autoPkg.vue": "AutoPkg",