Skip to content

Commit 09603c4

Browse files
author
piexlMax(奇淼
committed
feat: 增加 mcp Tools 自动测试工具
1 parent d4c8835 commit 09603c4

File tree

9 files changed

+181
-7
lines changed

9 files changed

+181
-7
lines changed

server/api/v1/system/auto_code_mcp.go

+99
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
package system
22

33
import (
4+
"fmt"
45
"github.com/flipped-aurora/gin-vue-admin/server/global"
6+
"github.com/flipped-aurora/gin-vue-admin/server/mcp/client"
57
"github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
68
"github.com/flipped-aurora/gin-vue-admin/server/model/system/request"
79
"github.com/gin-gonic/gin"
10+
"github.com/mark3labs/mcp-go/mcp"
811
)
912

1013
// Create
@@ -32,3 +35,99 @@ func (a *AutoCodeTemplateApi) MCP(c *gin.Context) {
3235
}
3336
response.OkWithMessage("创建成功,MCP Tool路径:"+toolFilePath, c)
3437
}
38+
39+
// Create
40+
// @Tags mcp
41+
// @Summary 自动McpTool
42+
// @Security ApiKeyAuth
43+
// @accept application/json
44+
// @Produce application/json
45+
// @Param data body request.AutoMcpTool true "创建自动代码"
46+
// @Success 200 {string} string "{"success":true,"data":{},"msg":"创建成功"}"
47+
// @Router /autoCode/mcpList [post]
48+
func (a *AutoCodeTemplateApi) MCPList(c *gin.Context) {
49+
50+
baseUrl := fmt.Sprintf("http://127.0.0.1:%d%s", global.GVA_CONFIG.System.Addr, global.GVA_CONFIG.MCP.SSEPath)
51+
52+
testClient, err := client.NewClient(baseUrl, "testClient", "v1.0.0", global.GVA_CONFIG.MCP.Name)
53+
defer testClient.Close()
54+
toolsRequest := mcp.ListToolsRequest{}
55+
56+
list, err := testClient.ListTools(c.Request.Context(), toolsRequest)
57+
58+
if err != nil {
59+
response.FailWithMessage("创建失败", c)
60+
global.GVA_LOG.Error(err.Error())
61+
return
62+
}
63+
response.OkWithData(list, c)
64+
}
65+
66+
// Create
67+
// @Tags mcp
68+
// @Summary 测试McpTool
69+
// @Security ApiKeyAuth
70+
// @accept application/json
71+
// @Produce application/json
72+
// @Param data body object true "调用MCP Tool的参数"
73+
// @Success 200 {object} response.Response "{"success":true,"data":{},"msg":"测试成功"}"
74+
// @Router /autoCode/mcpTest [post]
75+
func (a *AutoCodeTemplateApi) MCPTest(c *gin.Context) {
76+
// 定义接口请求结构
77+
var testRequest struct {
78+
Name string `json:"name" binding:"required"` // 工具名称
79+
Arguments map[string]interface{} `json:"arguments" binding:"required"` // 工具参数
80+
}
81+
82+
// 绑定JSON请求体
83+
if err := c.ShouldBindJSON(&testRequest); err != nil {
84+
response.FailWithMessage("参数解析失败:"+err.Error(), c)
85+
return
86+
}
87+
88+
// 创建MCP客户端
89+
baseUrl := fmt.Sprintf("http://127.0.0.1:%d%s", global.GVA_CONFIG.System.Addr, global.GVA_CONFIG.MCP.SSEPath)
90+
testClient, err := client.NewClient(baseUrl, "testClient", "v1.0.0", global.GVA_CONFIG.MCP.Name)
91+
if err != nil {
92+
response.FailWithMessage("创建MCP客户端失败:"+err.Error(), c)
93+
return
94+
}
95+
defer testClient.Close()
96+
97+
ctx := c.Request.Context()
98+
99+
// 初始化MCP连接
100+
initRequest := mcp.InitializeRequest{}
101+
initRequest.Params.ProtocolVersion = mcp.LATEST_PROTOCOL_VERSION
102+
initRequest.Params.ClientInfo = mcp.Implementation{
103+
Name: "testClient",
104+
Version: "v1.0.0",
105+
}
106+
107+
_, err = testClient.Initialize(ctx, initRequest)
108+
if err != nil {
109+
response.FailWithMessage("初始化MCP连接失败:"+err.Error(), c)
110+
return
111+
}
112+
113+
// 构建工具调用请求
114+
request := mcp.CallToolRequest{}
115+
request.Params.Name = testRequest.Name
116+
request.Params.Arguments = testRequest.Arguments
117+
118+
// 调用工具
119+
result, err := testClient.CallTool(ctx, request)
120+
if err != nil {
121+
response.FailWithMessage("工具调用失败:"+err.Error(), c)
122+
return
123+
}
124+
125+
// 处理响应结果
126+
if len(result.Content) == 0 {
127+
response.FailWithMessage("工具未返回任何内容", c)
128+
return
129+
}
130+
131+
// 返回结果
132+
response.OkWithData(result.Content, c)
133+
}

server/mcp/current_time.go

+44-2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ package mcpTool
22

33
import (
44
"context"
5+
"errors"
6+
"fmt"
57
"github.com/mark3labs/mcp-go/mcp"
68
"time"
79
)
@@ -16,13 +18,26 @@ type CurrentTime struct {
1618
// 获取当前系统时间
1719
func (t *CurrentTime) Handle(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
1820
// 获取当前系统时间
19-
currentTime := time.Now().Format("2006-01-02 15:04:05")
21+
22+
timezone, ok := request.Params.Arguments["timezone"].(string)
23+
24+
if !ok {
25+
return nil, errors.New("参数错误:timezone 必须是字符串类型")
26+
}
27+
// 根据timezone参数加载对应时区
28+
loc, err := loadTimeZone(timezone)
29+
if err != nil {
30+
return nil, err
31+
}
32+
33+
// 获取当前时间并转换为指定时区
34+
currentTime := time.Now().In(loc).Format("2006-01-02 15:04:05")
2035
//返回
2136
return &mcp.CallToolResult{
2237
Content: []mcp.Content{
2338
mcp.TextContent{
2439
Type: "text",
25-
Text: currentTime,
40+
Text: fmt.Sprintf("%s 时区的当前时间是:%s", timezone, currentTime),
2641
},
2742
},
2843
}, nil
@@ -37,3 +52,30 @@ func (t *CurrentTime) New() mcp.Tool {
3752
mcp.Enum("UTC", "CST", "PST", "EST", "GMT", "CET", "JST", "MST", "IST", "AST", "HST"),
3853
))
3954
}
55+
56+
// 将简写时区转换为IANA标准时区
57+
func loadTimeZone(timezone string) (*time.Location, error) {
58+
// 时区映射表
59+
timezoneMap := map[string]string{
60+
"UTC": "UTC",
61+
"CST": "Asia/Shanghai", // 中国标准时间
62+
"PST": "America/Los_Angeles",
63+
"EST": "America/New_York",
64+
"GMT": "GMT",
65+
"CET": "Europe/Paris",
66+
"JST": "Asia/Tokyo",
67+
"MST": "America/Denver",
68+
"IST": "Asia/Kolkata",
69+
"AST": "Asia/Riyadh", // 阿拉伯标准时间
70+
"HST": "Pacific/Honolulu",
71+
}
72+
73+
// 获取标准时区名称
74+
tzName, exists := timezoneMap[timezone]
75+
if !exists {
76+
return nil, errors.New("不支持的时区: " + timezone)
77+
}
78+
79+
// 加载时区
80+
return time.LoadLocation(tzName)
81+
}

server/mcp/get_nickname.go

+11-4
Original file line numberDiff line numberDiff line change
@@ -31,15 +31,15 @@ func (t *GetNickname) Handle(ctx context.Context, request mcp.CallToolRequest) (
3131
// 1. 参数验证
3232
username, ok := request.Params.Arguments["username"].(string)
3333
if !ok {
34-
return nil, errors.New("参数错误:username必须是字符串类型")
34+
return nil, errors.New("参数错误:username 必须是字符串类型")
3535
}
3636

3737
if username == "" {
38-
return nil, errors.New("参数错误:username不能为空")
38+
return nil, errors.New("参数错误:username 不能为空")
3939
}
4040

4141
// 2. 记录操作日志
42-
global.GVA_LOG.Info("getNickname工具被调用")
42+
global.GVA_LOG.Info("getNickname 工具被调用")
4343

4444
// 3. 优化查询,只选择需要的字段
4545
var user struct {
@@ -54,7 +54,14 @@ func (t *GetNickname) Handle(ctx context.Context, request mcp.CallToolRequest) (
5454
// 4. 优化错误处理
5555
if err != nil {
5656
if errors.Is(err, gorm.ErrRecordNotFound) {
57-
return nil, errors.New("用户不存在")
57+
return &mcp.CallToolResult{
58+
Content: []mcp.Content{
59+
mcp.TextContent{
60+
Type: "text",
61+
Text: fmt.Sprintf("用户 %s 不存在", username),
62+
},
63+
},
64+
}, nil
5865
}
5966
global.GVA_LOG.Error("数据库查询错误")
6067
return nil, errors.New("系统错误,请稍后再试")

server/router/system/sys_auto_code.go

+3-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@ func (s *AutoCodeRouter) InitAutoCodeRouter(Router *gin.RouterGroup, RouterPubli
2020
autoCodeRouter.POST("addFunc", autoCodeTemplateApi.AddFunc) // 为代码插入方法
2121
}
2222
{
23-
autoCodeRouter.POST("mcp", autoCodeTemplateApi.MCP) // 自动创建Mcp Tool模板
23+
autoCodeRouter.POST("mcp", autoCodeTemplateApi.MCP) // 自动创建Mcp Tool模板
24+
autoCodeRouter.POST("mcpList", autoCodeTemplateApi.MCPList) // 获取MCP ToolList
25+
autoCodeRouter.POST("mcpTest", autoCodeTemplateApi.MCPTest) // MCP 工具测试
2426
}
2527
{
2628
autoCodeRouter.POST("getPackage", autoCodePackageApi.All) // 获取package包

server/source/system/api.go

+2
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,8 @@ func (i *initApi) InitializeData(ctx context.Context) (context.Context, error) {
118118
{ApiGroup: "代码生成器", Method: "POST", Path: "/autoCode/installPlugin", Description: "安装插件"},
119119
{ApiGroup: "代码生成器", Method: "POST", Path: "/autoCode/pubPlug", Description: "打包插件"},
120120
{ApiGroup: "代码生成器", Method: "POST", Path: "/autoCode/mcp", Description: "自动生成 MCP Tool 模板"},
121+
{ApiGroup: "代码生成器", Method: "POST", Path: "/autoCode/mcpTest", Description: "MCP Tool 测试"},
122+
{ApiGroup: "代码生成器", Method: "POST", Path: "/autoCode/mcpList", Description: "获取 MCP ToolList"},
121123

122124
{ApiGroup: "模板配置", Method: "POST", Path: "/autoCode/createPackage", Description: "配置模板"},
123125
{ApiGroup: "模板配置", Method: "GET", Path: "/autoCode/getTemplates", Description: "获取模板文件"},

server/source/system/casbin.go

+2
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,8 @@ func (i *initCasbin) InitializeData(ctx context.Context) (context.Context, error
131131
{Ptype: "p", V0: "888", V1: "/autoCode/pubPlug", V2: "POST"},
132132
{Ptype: "p", V0: "888", V1: "/autoCode/addFunc", V2: "POST"},
133133
{Ptype: "p", V0: "888", V1: "/autoCode/mcp", V2: "POST"},
134+
{Ptype: "p", V0: "888", V1: "/autoCode/mcpTest", V2: "POST"},
135+
{Ptype: "p", V0: "888", V1: "/autoCode/mcpList", V2: "POST"},
134136

135137
{Ptype: "p", V0: "888", V1: "/sysDictionaryDetail/findSysDictionaryDetail", V2: "GET"},
136138
{Ptype: "p", V0: "888", V1: "/sysDictionaryDetail/updateSysDictionaryDetail", V2: "PUT"},

server/source/system/menu.go

+1
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ func (i *initMenu) InitializeData(ctx context.Context) (next context.Context, er
101101
{MenuLevel: 1, Hidden: false, ParentId: menuNameMap["systemTools"], Path: "exportTemplate", Name: "exportTemplate", Component: "view/systemTools/exportTemplate/exportTemplate.vue", Sort: 5, Meta: Meta{Title: "导出模板", Icon: "reading"}},
102102
{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"}},
103103
{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"}},
104+
{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"}},
104105

105106
{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"}},
106107
{MenuLevel: 1, Hidden: false, ParentId: menuNameMap["plugin"], Path: "installPlugin", Name: "installPlugin", Component: "view/systemTools/installPlugin/index.vue", Sort: 1, Meta: Meta{Title: "插件安装", Icon: "box"}},

web/src/api/autoCode.js

+18
Original file line numberDiff line numberDiff line change
@@ -215,3 +215,21 @@ export const mcp = (data) => {
215215
data
216216
})
217217
}
218+
219+
220+
export const mcpList = (data) => {
221+
return service({
222+
url: '/autoCode/mcpList',
223+
method: 'post',
224+
data
225+
})
226+
}
227+
228+
229+
export const mcpTest = (data) => {
230+
return service({
231+
url: '/autoCode/mcpTest',
232+
method: 'post',
233+
data
234+
})
235+
}

web/src/pathInfo.json

+1
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@
5858
"/src/view/systemTools/autoCode/component/previewCodeDialog.vue": "PreviewCodeDialog",
5959
"/src/view/systemTools/autoCode/index.vue": "AutoCode",
6060
"/src/view/systemTools/autoCode/mcp.vue": "Mcp",
61+
"/src/view/systemTools/autoCode/mcpToolsTest.vue": "McpToolsTest",
6162
"/src/view/systemTools/autoCode/picture.vue": "Picture",
6263
"/src/view/systemTools/autoCodeAdmin/index.vue": "AutoCodeAdmin",
6364
"/src/view/systemTools/autoPkg/autoPkg.vue": "AutoPkg",

0 commit comments

Comments
 (0)