Skip to content

Commit 14f937f

Browse files
committed
feat: mcp能力调整为 streamhttp的独立服务 不再依赖gva本体
1 parent 324aaa3 commit 14f937f

File tree

30 files changed

+1570
-1354
lines changed

30 files changed

+1570
-1354
lines changed

server/api/v1/example/enter.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,14 @@ import "github.com/flipped-aurora/gin-vue-admin/server/service"
44

55
type ApiGroup struct {
66
CustomerApi
7-
FileUploadAndDownloadApi
7+
88
AttachmentCategoryApi
9+
FileUploadAndDownloadApi
910
}
1011

1112
var (
12-
customerService = service.ServiceGroupApp.ExampleServiceGroup.CustomerService
13-
fileUploadAndDownloadService = service.ServiceGroupApp.ExampleServiceGroup.FileUploadAndDownloadService
13+
customerService = service.ServiceGroupApp.ExampleServiceGroup.CustomerService
14+
1415
attachmentCategoryService = service.ServiceGroupApp.ExampleServiceGroup.AttachmentCategoryService
16+
fileUploadAndDownloadService = service.ServiceGroupApp.ExampleServiceGroup.FileUploadAndDownloadService
1517
)
Lines changed: 44 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,18 @@
11
package system
22

33
import (
4-
"fmt"
54
"github.com/flipped-aurora/gin-vue-admin/server/global"
5+
mcpTool "github.com/flipped-aurora/gin-vue-admin/server/mcp"
66
"github.com/flipped-aurora/gin-vue-admin/server/mcp/client"
77
"github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
88
"github.com/flipped-aurora/gin-vue-admin/server/model/system/request"
99
"github.com/gin-gonic/gin"
1010
"github.com/mark3labs/mcp-go/mcp"
1111
)
1212

13-
// Create
14-
// @Tags mcp
15-
// @Summary 自动McpTool
16-
// @Security ApiKeyAuth
17-
// @accept application/json
18-
// @Produce application/json
19-
// @Param data body request.AutoMcpTool true "创建自动代码"
20-
// @Success 200 {string} string "{"success":true,"data":{},"msg":"创建成功"}"
21-
// @Router /autoCode/mcp [post]
2213
func (a *AutoCodeTemplateApi) MCP(c *gin.Context) {
2314
var info request.AutoMcpTool
24-
err := c.ShouldBindJSON(&info)
25-
if err != nil {
15+
if err := c.ShouldBindJSON(&info); err != nil {
2616
response.FailWithMessage(err.Error(), c)
2717
return
2818
}
@@ -36,109 +26,88 @@ func (a *AutoCodeTemplateApi) MCP(c *gin.Context) {
3626
response.OkWithMessage("创建成功,MCP Tool路径:"+toolFilePath, c)
3727
}
3828

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]
4829
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)
30+
baseURL := mcpTool.ResolveMCPServiceURL()
31+
testClient, err := client.NewClient(baseURL, "testClient", "v1.0.0", global.GVA_CONFIG.MCP.Name, incomingMCPHeaders(c))
32+
if err != nil {
33+
response.FailWithMessage("连接MCP服务失败:"+err.Error(), c)
34+
return
35+
}
5336
defer testClient.Close()
54-
toolsRequest := mcp.ListToolsRequest{}
55-
56-
list, err := testClient.ListTools(c.Request.Context(), toolsRequest)
5737

38+
list, err := testClient.ListTools(c.Request.Context(), mcp.ListToolsRequest{})
5839
if err != nil {
59-
response.FailWithMessage("创建失败", c)
60-
global.GVA_LOG.Error(err.Error())
40+
response.FailWithMessage("获取工具列表失败:"+err.Error(), c)
6141
return
6242
}
6343

44+
authHeader := mcpTool.ConfiguredAuthHeader()
6445
mcpServerConfig := map[string]interface{}{
6546
"mcpServers": map[string]interface{}{
66-
global.GVA_CONFIG.MCP.Name: map[string]string{
67-
"url": baseUrl,
47+
global.GVA_CONFIG.MCP.Name: map[string]interface{}{
48+
"url": baseURL,
49+
"headers": map[string]string{
50+
authHeader: "${YOUR_GVA_TOKEN}",
51+
},
6852
},
6953
},
7054
}
55+
7156
response.OkWithData(gin.H{
7257
"mcpServerConfig": mcpServerConfig,
7358
"list": list,
7459
}, c)
7560
}
7661

77-
// Create
78-
// @Tags mcp
79-
// @Summary 测试McpTool
80-
// @Security ApiKeyAuth
81-
// @accept application/json
82-
// @Produce application/json
83-
// @Param data body object true "调用MCP Tool的参数"
84-
// @Success 200 {object} response.Response "{"success":true,"data":{},"msg":"测试成功"}"
85-
// @Router /autoCode/mcpTest [post]
62+
func (a *AutoCodeTemplateApi) MCPRoutes(c *gin.Context) {
63+
response.OkWithData(gin.H{
64+
"routes": global.GVA_ROUTERS,
65+
}, c)
66+
}
67+
8668
func (a *AutoCodeTemplateApi) MCPTest(c *gin.Context) {
87-
// 定义接口请求结构
8869
var testRequest struct {
89-
Name string `json:"name" binding:"required"` // 工具名称
90-
Arguments map[string]interface{} `json:"arguments" binding:"required"` // 工具参数
70+
Name string `json:"name" binding:"required"`
71+
Arguments map[string]interface{} `json:"arguments" binding:"required"`
9172
}
92-
93-
// 绑定JSON请求体
9473
if err := c.ShouldBindJSON(&testRequest); err != nil {
9574
response.FailWithMessage("参数解析失败:"+err.Error(), c)
9675
return
9776
}
9877

99-
// 创建MCP客户端
100-
baseUrl := fmt.Sprintf("http://127.0.0.1:%d%s", global.GVA_CONFIG.System.Addr, global.GVA_CONFIG.MCP.SSEPath)
101-
testClient, err := client.NewClient(baseUrl, "testClient", "v1.0.0", global.GVA_CONFIG.MCP.Name)
78+
baseURL := mcpTool.ResolveMCPServiceURL()
79+
testClient, err := client.NewClient(baseURL, "testClient", "v1.0.0", global.GVA_CONFIG.MCP.Name, incomingMCPHeaders(c))
10280
if err != nil {
103-
response.FailWithMessage("创建MCP客户端失败:"+err.Error(), c)
81+
response.FailWithMessage("连接MCP服务失败:"+err.Error(), c)
10482
return
10583
}
10684
defer testClient.Close()
10785

108-
ctx := c.Request.Context()
109-
110-
// 初始化MCP连接
111-
initRequest := mcp.InitializeRequest{}
112-
initRequest.Params.ProtocolVersion = mcp.LATEST_PROTOCOL_VERSION
113-
initRequest.Params.ClientInfo = mcp.Implementation{
114-
Name: "testClient",
115-
Version: "v1.0.0",
116-
}
117-
118-
_, err = testClient.Initialize(ctx, initRequest)
119-
if err != nil {
120-
response.FailWithMessage("初始化MCP连接失败:"+err.Error(), c)
121-
return
122-
}
123-
124-
// 构建工具调用请求
125-
request := mcp.CallToolRequest{}
126-
request.Params.Name = testRequest.Name
127-
request.Params.Arguments = testRequest.Arguments
86+
callRequest := mcp.CallToolRequest{}
87+
callRequest.Params.Name = testRequest.Name
88+
callRequest.Params.Arguments = testRequest.Arguments
12889

129-
// 调用工具
130-
result, err := testClient.CallTool(ctx, request)
90+
result, err := testClient.CallTool(c.Request.Context(), callRequest)
13191
if err != nil {
13292
response.FailWithMessage("工具调用失败:"+err.Error(), c)
13393
return
13494
}
135-
136-
// 处理响应结果
13795
if len(result.Content) == 0 {
13896
response.FailWithMessage("工具未返回任何内容", c)
13997
return
14098
}
14199

142-
// 返回结果
143100
response.OkWithData(result.Content, c)
144101
}
102+
103+
func incomingMCPHeaders(c *gin.Context) map[string]string {
104+
headerName := mcpTool.ConfiguredAuthHeader()
105+
headerValue := c.GetHeader(headerName)
106+
if headerValue == "" {
107+
return nil
108+
}
109+
110+
return map[string]string{
111+
headerName: headerValue,
112+
}
113+
}

server/cmd/mcp/config.go

Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
package main
2+
3+
import (
4+
"bufio"
5+
"errors"
6+
"flag"
7+
"fmt"
8+
"io"
9+
"os"
10+
"path/filepath"
11+
"strings"
12+
13+
"github.com/flipped-aurora/gin-vue-admin/server/config"
14+
"github.com/flipped-aurora/gin-vue-admin/server/global"
15+
"gopkg.in/yaml.v3"
16+
)
17+
18+
type standaloneConfig struct {
19+
MCP config.MCP `yaml:"mcp"`
20+
AutoCode config.Autocode `yaml:"autocode"`
21+
}
22+
23+
func loadStandaloneConfig() (string, error) {
24+
configPath, err := resolveConfigPath()
25+
if err != nil {
26+
return "", err
27+
}
28+
29+
content, err := os.ReadFile(configPath)
30+
if err != nil {
31+
return "", fmt.Errorf("读取 MCP 配置失败: %w", err)
32+
}
33+
34+
var cfg standaloneConfig
35+
if err := yaml.Unmarshal(content, &cfg); err != nil {
36+
return "", fmt.Errorf("解析 MCP 配置失败: %w", err)
37+
}
38+
39+
applyStandaloneDefaults(configPath, &cfg)
40+
41+
global.GVA_CONFIG.MCP = cfg.MCP
42+
global.GVA_CONFIG.AutoCode = cfg.AutoCode
43+
44+
return configPath, nil
45+
}
46+
47+
func resolveConfigPath() (string, error) {
48+
explicit, err := parseConfigFlag(os.Args[1:])
49+
if err != nil {
50+
return "", err
51+
}
52+
if explicit != "" {
53+
return filepath.Abs(explicit)
54+
}
55+
56+
if envPath := strings.TrimSpace(os.Getenv("GVA_MCP_CONFIG")); envPath != "" {
57+
return filepath.Abs(envPath)
58+
}
59+
60+
wd, _ := os.Getwd()
61+
exe, _ := os.Executable()
62+
exeDir := filepath.Dir(exe)
63+
64+
candidates := []string{
65+
filepath.Join(wd, "config.yaml"),
66+
filepath.Join(wd, "mcp.yaml"),
67+
filepath.Join(wd, "cmd", "mcp", "config.yaml"),
68+
filepath.Join(wd, "server", "cmd", "mcp", "config.yaml"),
69+
filepath.Join(exeDir, "config.yaml"),
70+
filepath.Join(exeDir, "mcp.yaml"),
71+
}
72+
73+
for _, candidate := range candidates {
74+
if candidate == "" {
75+
continue
76+
}
77+
if info, err := os.Stat(candidate); err == nil && !info.IsDir() {
78+
return filepath.Abs(candidate)
79+
}
80+
}
81+
82+
return "", errors.New("未找到 MCP 独立配置文件,请在当前目录、cmd/mcp 目录或通过 -config / GVA_MCP_CONFIG 指定 config.yaml")
83+
}
84+
85+
func parseConfigFlag(args []string) (string, error) {
86+
fs := flag.NewFlagSet("gva-mcp", flag.ContinueOnError)
87+
fs.SetOutput(io.Discard)
88+
89+
var configPath string
90+
fs.StringVar(&configPath, "c", "", "MCP config file path")
91+
fs.StringVar(&configPath, "config", "", "MCP config file path")
92+
if err := fs.Parse(args); err != nil {
93+
return "", err
94+
}
95+
96+
return strings.TrimSpace(configPath), nil
97+
}
98+
99+
func applyStandaloneDefaults(configPath string, cfg *standaloneConfig) {
100+
if cfg.MCP.Name == "" {
101+
cfg.MCP.Name = "GVA_MCP"
102+
}
103+
if cfg.MCP.Version == "" {
104+
cfg.MCP.Version = "v1.0.0"
105+
}
106+
if cfg.MCP.Path == "" {
107+
cfg.MCP.Path = "/mcp"
108+
}
109+
if cfg.MCP.Addr == 0 {
110+
cfg.MCP.Addr = 8889
111+
}
112+
if cfg.MCP.AuthHeader == "" {
113+
cfg.MCP.AuthHeader = "x-token"
114+
}
115+
if cfg.MCP.RequestTimeout <= 0 {
116+
cfg.MCP.RequestTimeout = 15
117+
}
118+
if cfg.MCP.UpstreamBaseURL == "" {
119+
cfg.MCP.UpstreamBaseURL = "http://127.0.0.1:8888"
120+
}
121+
if cfg.MCP.BaseURL == "" {
122+
cfg.MCP.BaseURL = fmt.Sprintf("http://127.0.0.1:%d%s", cfg.MCP.Addr, cfg.MCP.Path)
123+
}
124+
125+
configDir := filepath.Dir(configPath)
126+
if cfg.AutoCode.Server == "" {
127+
cfg.AutoCode.Server = "server"
128+
}
129+
if cfg.AutoCode.Web == "" {
130+
cfg.AutoCode.Web = "web/src"
131+
}
132+
if cfg.AutoCode.Root == "" {
133+
if root, err := detectProjectRoot(configDir); err == nil {
134+
cfg.AutoCode.Root = root
135+
}
136+
} else if !filepath.IsAbs(cfg.AutoCode.Root) {
137+
cfg.AutoCode.Root = filepath.Clean(filepath.Join(configDir, cfg.AutoCode.Root))
138+
}
139+
140+
if cfg.AutoCode.Module == "" && cfg.AutoCode.Root != "" {
141+
goModPath := filepath.Join(cfg.AutoCode.Root, cfg.AutoCode.Server, "go.mod")
142+
if module, err := detectGoModule(goModPath); err == nil {
143+
cfg.AutoCode.Module = module
144+
}
145+
}
146+
}
147+
148+
func detectProjectRoot(startDir string) (string, error) {
149+
dir := startDir
150+
for {
151+
serverDir := filepath.Join(dir, "server")
152+
webDir := filepath.Join(dir, "web")
153+
if isDir(serverDir) && isDir(webDir) {
154+
return dir, nil
155+
}
156+
157+
parent := filepath.Dir(dir)
158+
if parent == dir {
159+
break
160+
}
161+
dir = parent
162+
}
163+
164+
return "", errors.New("未能自动识别项目根目录,请在 MCP 配置中设置 autocode.root")
165+
}
166+
167+
func detectGoModule(goModPath string) (string, error) {
168+
file, err := os.Open(goModPath)
169+
if err != nil {
170+
return "", err
171+
}
172+
defer file.Close()
173+
174+
scanner := bufio.NewScanner(file)
175+
for scanner.Scan() {
176+
line := strings.TrimSpace(scanner.Text())
177+
if strings.HasPrefix(line, "module ") {
178+
return strings.TrimSpace(strings.TrimPrefix(line, "module ")), nil
179+
}
180+
}
181+
182+
if err := scanner.Err(); err != nil {
183+
return "", err
184+
}
185+
186+
return "", errors.New("go.mod 中未找到 module 定义")
187+
}
188+
189+
func isDir(path string) bool {
190+
info, err := os.Stat(path)
191+
return err == nil && info.IsDir()
192+
}

0 commit comments

Comments
 (0)