Skip to content

Commit 53ea832

Browse files
xpzouyingclaude
andauthored
refactor: 迁移到官方 MCP SDK (#167)
- 添加官方 SDK 依赖 github.com/modelcontextprotocol/go-sdk v0.7.0 - 新增 mcp_server.go 使用官方 SDK 注册 8 个 MCP 工具 - 删除自实现的 streamable_http.go(约 400 行) - 更新 routes.go 使用 mcp.NewStreamableHTTPHandler - 优化服务器优雅关闭逻辑(5秒超时 + 警告日志) - 清理 types.go 中的 JSON-RPC 相关类型 🤖 Generated with [Claude Code](https://claude.ai/code) Co-authored-by: Claude <noreply@anthropic.com>
1 parent 529ee71 commit 53ea832

File tree

8 files changed

+270
-459
lines changed

8 files changed

+270
-459
lines changed

app_server.go

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,21 +9,28 @@ import (
99
"time"
1010

1111
"github.com/gin-gonic/gin"
12+
"github.com/modelcontextprotocol/go-sdk/mcp"
1213
"github.com/sirupsen/logrus"
1314
)
1415

1516
// AppServer 应用服务器结构体,封装所有服务和处理器
1617
type AppServer struct {
1718
xiaohongshuService *XiaohongshuService
19+
mcpServer *mcp.Server
1820
router *gin.Engine
1921
httpServer *http.Server
2022
}
2123

2224
// NewAppServer 创建新的应用服务器实例
2325
func NewAppServer(xiaohongshuService *XiaohongshuService) *AppServer {
24-
return &AppServer{
26+
appServer := &AppServer{
2527
xiaohongshuService: xiaohongshuService,
2628
}
29+
30+
// 初始化 MCP Server(需要在创建 appServer 之后,因为工具注册需要访问 appServer)
31+
appServer.mcpServer = InitMCPServer(appServer)
32+
33+
return appServer
2734
}
2835

2936
// Start 启动服务器
@@ -51,16 +58,14 @@ func (s *AppServer) Start(port string) error {
5158

5259
logrus.Infof("正在关闭服务器...")
5360

54-
// 优雅关闭
55-
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
61+
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
5662
defer cancel()
5763

58-
// 关闭 HTTP 服务器
5964
if err := s.httpServer.Shutdown(ctx); err != nil {
60-
logrus.Errorf("服务器关闭失败: %v", err)
61-
return err
65+
logrus.Warnf("等待连接关闭超时,强制退出: %v", err)
66+
} else {
67+
logrus.Infof("服务器已优雅关闭")
6268
}
6369

64-
logrus.Infof("服务器已关闭")
6570
return nil
6671
}

go.mod

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ require (
66
github.com/gin-gonic/gin v1.10.1
77
github.com/go-rod/rod v0.116.2
88
github.com/h2non/filetype v1.1.3
9+
github.com/mattn/go-runewidth v0.0.16
10+
github.com/modelcontextprotocol/go-sdk v0.7.0
911
github.com/pkg/errors v0.9.1
1012
github.com/sirupsen/logrus v1.9.3
1113
github.com/stretchr/testify v1.10.0
@@ -25,18 +27,19 @@ require (
2527
github.com/go-playground/validator/v10 v10.20.0 // indirect
2628
github.com/go-rod/stealth v0.4.9 // indirect
2729
github.com/goccy/go-json v0.10.2 // indirect
30+
github.com/google/jsonschema-go v0.3.0 // indirect
2831
github.com/json-iterator/go v1.1.12 // indirect
2932
github.com/klauspost/cpuid/v2 v2.2.7 // indirect
3033
github.com/leodido/go-urn v1.4.0 // indirect
3134
github.com/mattn/go-isatty v0.0.20 // indirect
32-
github.com/mattn/go-runewidth v0.0.16 // indirect
3335
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
3436
github.com/modern-go/reflect2 v1.0.2 // indirect
3537
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
3638
github.com/pmezard/go-difflib v1.0.0 // indirect
3739
github.com/rivo/uniseg v0.2.0 // indirect
3840
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
3941
github.com/ugorji/go/codec v1.2.12 // indirect
42+
github.com/yosida95/uritemplate/v3 v3.0.2 // indirect
4043
github.com/ysmood/fetchup v0.2.3 // indirect
4144
github.com/ysmood/goob v0.4.0 // indirect
4245
github.com/ysmood/got v0.41.0 // indirect

go.sum

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,11 @@ github.com/go-rod/stealth v0.4.9 h1:X2PmQk4DUF2wzw6GOsWjW/glb8K5ebnftbEvLh7MlZ4=
3030
github.com/go-rod/stealth v0.4.9/go.mod h1:eAzyvw8c0iAd5nJJsSWeh0fQ5z94vCIfdi1hUmYDimc=
3131
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
3232
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
33-
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
34-
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
33+
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
34+
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
3535
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
36+
github.com/google/jsonschema-go v0.3.0 h1:6AH2TxVNtk3IlvkkhjrtbUc4S8AvO0Xii0DxIygDg+Q=
37+
github.com/google/jsonschema-go v0.3.0/go.mod h1:r5quNTdLOYEz95Ru18zA0ydNbBuYoo9tgaYcxEYhJVE=
3638
github.com/h2non/filetype v1.1.3 h1:FKkx9QbD7HR/zjK1Ia5XiBsq9zdLi5Kf3zGyFTAFkGg=
3739
github.com/h2non/filetype v1.1.3/go.mod h1:319b3zT68BvV+WRj7cwy856M2ehB3HqNOt6sy1HndBY=
3840
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
@@ -47,6 +49,8 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE
4749
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
4850
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
4951
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
52+
github.com/modelcontextprotocol/go-sdk v0.7.0 h1:XEQfn3bDx2cAdSUKty3tYEMll5dtRgBUDX88Q65fai0=
53+
github.com/modelcontextprotocol/go-sdk v0.7.0/go.mod h1:nYtYQroQ2KQiM0/SbyEPUWQ6xs4B95gJjEalc9AQyOs=
5054
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
5155
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
5256
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
@@ -79,16 +83,12 @@ github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS
7983
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
8084
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
8185
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
82-
github.com/xpzouying/headless_browser v0.0.2 h1:sLc4gqUT/5IyTruYIOfCW4aZLinq38hIdUHCHem1KYo=
83-
github.com/xpzouying/headless_browser v0.0.2/go.mod h1:bQTSzGYHIipa1zwToMlOGHcXWDlvw8y33Cx5zzElekc=
84-
github.com/xpzouying/headless_browser v0.1.0 h1:0FyMIzhe/If/VhEdDrs7T1fqm1gOZSCFrmMXI/1JM58=
85-
github.com/xpzouying/headless_browser v0.1.0/go.mod h1:bQTSzGYHIipa1zwToMlOGHcXWDlvw8y33Cx5zzElekc=
8686
github.com/xpzouying/headless_browser v0.2.0 h1:EmuHXDVzx0tAevHJUdETs8iT/eK+QqrLiybvGd1xZDA=
8787
github.com/xpzouying/headless_browser v0.2.0/go.mod h1:bQTSzGYHIipa1zwToMlOGHcXWDlvw8y33Cx5zzElekc=
88+
github.com/yosida95/uritemplate/v3 v3.0.2 h1:Ed3Oyj9yrmi9087+NczuL5BwkIc4wvTb5zIM+UJPGz4=
89+
github.com/yosida95/uritemplate/v3 v3.0.2/go.mod h1:ILOh0sOhIJR3+L/8afwt/kE++YT040gmv5BQTMR2HP4=
8890
github.com/ysmood/fetchup v0.2.3 h1:ulX+SonA0Vma5zUFXtv52Kzip/xe7aj4vqT5AJwQ+ZQ=
8991
github.com/ysmood/fetchup v0.2.3/go.mod h1:xhibcRKziSvol0H1/pj33dnKrYyI2ebIvz5cOOkYGns=
90-
github.com/ysmood/fetchup v0.5.2 h1:P9w3OIA7RSNEEFvEmOiTq09IOu42C96PMyZ1MWd8TAs=
91-
github.com/ysmood/fetchup v0.5.2/go.mod h1:yCv8s8itjsCul1LGXJ1Q+8EQnZcVjfbZ4+l1zDm4StE=
9292
github.com/ysmood/goob v0.4.0 h1:HsxXhyLBeGzWXnqVKtmT9qM7EuVs/XOgkX7T6r1o1AQ=
9393
github.com/ysmood/goob v0.4.0/go.mod h1:u6yx7ZhS4Exf2MwciFr6nIM8knHQIE22lFpWHnfql18=
9494
github.com/ysmood/gop v0.0.2/go.mod h1:rr5z2z27oGEbyB787hpEcx4ab8cCiPnKxn0SUHt6xzk=
@@ -114,14 +114,12 @@ golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
114114
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
115115
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
116116
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
117-
golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI=
118-
golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
119117
golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k=
120118
golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
121119
golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk=
122120
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
123-
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
124-
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
121+
golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo=
122+
golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg=
125123
google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg=
126124
google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
127125
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=

main.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,11 @@ func main() {
1212
var (
1313
headless bool
1414
binPath string // 浏览器二进制文件路径
15+
port string
1516
)
1617
flag.BoolVar(&headless, "headless", true, "是否无头模式")
1718
flag.StringVar(&binPath, "bin", "", "浏览器二进制文件路径")
19+
flag.StringVar(&port, "port", ":18060", "端口")
1820
flag.Parse()
1921

2022
if len(binPath) == 0 {
@@ -29,7 +31,7 @@ func main() {
2931

3032
// 创建并启动应用服务器
3133
appServer := NewAppServer(xiaohongshuService)
32-
if err := appServer.Start(":18060"); err != nil {
34+
if err := appServer.Start(port); err != nil {
3335
logrus.Fatalf("failed to run server: %v", err)
3436
}
3537
}

mcp_server.go

Lines changed: 226 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,226 @@
1+
package main
2+
3+
import (
4+
"context"
5+
"encoding/base64"
6+
7+
"github.com/modelcontextprotocol/go-sdk/mcp"
8+
"github.com/sirupsen/logrus"
9+
)
10+
11+
// MCP 工具参数结构体定义
12+
13+
// PublishContentArgs 发布内容的参数
14+
type PublishContentArgs struct {
15+
Title string `json:"title" jsonschema:"内容标题(小红书限制:最多20个中文字或英文单词)"`
16+
Content string `json:"content" jsonschema:"正文内容,不包含以#开头的标签内容,所有话题标签都用tags参数来生成和提供即可"`
17+
Images []string `json:"images" jsonschema:"图片路径列表(至少需要1张图片)。支持两种方式:1. HTTP/HTTPS图片链接(自动下载);2. 本地图片绝对路径(推荐,如:/Users/user/image.jpg)"`
18+
Tags []string `json:"tags,omitempty" jsonschema:"话题标签列表(可选参数),如 [美食, 旅行, 生活]"`
19+
}
20+
21+
// SearchFeedsArgs 搜索内容的参数
22+
type SearchFeedsArgs struct {
23+
Keyword string `json:"keyword" jsonschema:"搜索关键词"`
24+
}
25+
26+
// FeedDetailArgs 获取Feed详情的参数
27+
type FeedDetailArgs struct {
28+
FeedID string `json:"feed_id" jsonschema:"小红书笔记ID,从Feed列表获取"`
29+
XsecToken string `json:"xsec_token" jsonschema:"访问令牌,从Feed列表的xsecToken字段获取"`
30+
}
31+
32+
// UserProfileArgs 获取用户主页的参数
33+
type UserProfileArgs struct {
34+
UserID string `json:"user_id" jsonschema:"小红书用户ID,从Feed列表获取"`
35+
XsecToken string `json:"xsec_token" jsonschema:"访问令牌,从Feed列表的xsecToken字段获取"`
36+
}
37+
38+
// PostCommentArgs 发表评论的参数
39+
type PostCommentArgs struct {
40+
FeedID string `json:"feed_id" jsonschema:"小红书笔记ID,从Feed列表获取"`
41+
XsecToken string `json:"xsec_token" jsonschema:"访问令牌,从Feed列表的xsecToken字段获取"`
42+
Content string `json:"content" jsonschema:"评论内容"`
43+
}
44+
45+
// InitMCPServer 初始化 MCP Server
46+
func InitMCPServer(appServer *AppServer) *mcp.Server {
47+
// 创建 MCP Server
48+
server := mcp.NewServer(
49+
&mcp.Implementation{
50+
Name: "xiaohongshu-mcp",
51+
Version: "2.0.0",
52+
},
53+
nil,
54+
)
55+
56+
// 注册所有工具
57+
registerTools(server, appServer)
58+
59+
logrus.Info("MCP Server initialized with official SDK")
60+
61+
return server
62+
}
63+
64+
// registerTools 注册所有 MCP 工具
65+
func registerTools(server *mcp.Server, appServer *AppServer) {
66+
// 工具 1: 检查登录状态
67+
mcp.AddTool(server,
68+
&mcp.Tool{
69+
Name: "check_login_status",
70+
Description: "检查小红书登录状态",
71+
},
72+
func(ctx context.Context, req *mcp.CallToolRequest, _ any) (*mcp.CallToolResult, any, error) {
73+
result := appServer.handleCheckLoginStatus(ctx)
74+
return convertToMCPResult(result), nil, nil
75+
},
76+
)
77+
78+
// 工具 2: 获取登录二维码
79+
mcp.AddTool(server,
80+
&mcp.Tool{
81+
Name: "get_login_qrcode",
82+
Description: "获取登录二维码(返回 Base64 图片和超时时间)",
83+
},
84+
func(ctx context.Context, req *mcp.CallToolRequest, _ any) (*mcp.CallToolResult, any, error) {
85+
result := appServer.handleGetLoginQrcode(ctx)
86+
return convertToMCPResult(result), nil, nil
87+
},
88+
)
89+
90+
// 工具 3: 发布内容
91+
mcp.AddTool(server,
92+
&mcp.Tool{
93+
Name: "publish_content",
94+
Description: "发布小红书图文内容",
95+
},
96+
func(ctx context.Context, req *mcp.CallToolRequest, args PublishContentArgs) (*mcp.CallToolResult, any, error) {
97+
// 转换参数格式到现有的 handler
98+
argsMap := map[string]interface{}{
99+
"title": args.Title,
100+
"content": args.Content,
101+
"images": convertStringsToInterfaces(args.Images),
102+
"tags": convertStringsToInterfaces(args.Tags),
103+
}
104+
result := appServer.handlePublishContent(ctx, argsMap)
105+
return convertToMCPResult(result), nil, nil
106+
},
107+
)
108+
109+
// 工具 4: 获取Feed列表
110+
mcp.AddTool(server,
111+
&mcp.Tool{
112+
Name: "list_feeds",
113+
Description: "获取用户发布的内容列表",
114+
},
115+
func(ctx context.Context, req *mcp.CallToolRequest, _ any) (*mcp.CallToolResult, any, error) {
116+
result := appServer.handleListFeeds(ctx)
117+
return convertToMCPResult(result), nil, nil
118+
},
119+
)
120+
121+
// 工具 5: 搜索内容
122+
mcp.AddTool(server,
123+
&mcp.Tool{
124+
Name: "search_feeds",
125+
Description: "搜索小红书内容(需要已登录)",
126+
},
127+
func(ctx context.Context, req *mcp.CallToolRequest, args SearchFeedsArgs) (*mcp.CallToolResult, any, error) {
128+
argsMap := map[string]interface{}{
129+
"keyword": args.Keyword,
130+
}
131+
result := appServer.handleSearchFeeds(ctx, argsMap)
132+
return convertToMCPResult(result), nil, nil
133+
},
134+
)
135+
136+
// 工具 6: 获取Feed详情
137+
mcp.AddTool(server,
138+
&mcp.Tool{
139+
Name: "get_feed_detail",
140+
Description: "获取小红书笔记详情,返回笔记内容、图片、作者信息、互动数据(点赞/收藏/分享数)及评论列表",
141+
},
142+
func(ctx context.Context, req *mcp.CallToolRequest, args FeedDetailArgs) (*mcp.CallToolResult, any, error) {
143+
argsMap := map[string]interface{}{
144+
"feed_id": args.FeedID,
145+
"xsec_token": args.XsecToken,
146+
}
147+
result := appServer.handleGetFeedDetail(ctx, argsMap)
148+
return convertToMCPResult(result), nil, nil
149+
},
150+
)
151+
152+
// 工具 7: 获取用户主页
153+
mcp.AddTool(server,
154+
&mcp.Tool{
155+
Name: "user_profile",
156+
Description: "获取小红书用户主页,返回用户基本信息,关注、粉丝、获赞量及其笔记内容",
157+
},
158+
func(ctx context.Context, req *mcp.CallToolRequest, args UserProfileArgs) (*mcp.CallToolResult, any, error) {
159+
argsMap := map[string]interface{}{
160+
"user_id": args.UserID,
161+
"xsec_token": args.XsecToken,
162+
}
163+
result := appServer.handleUserProfile(ctx, argsMap)
164+
return convertToMCPResult(result), nil, nil
165+
},
166+
)
167+
168+
// 工具 8: 发表评论
169+
mcp.AddTool(server,
170+
&mcp.Tool{
171+
Name: "post_comment_to_feed",
172+
Description: "发表评论到小红书笔记",
173+
},
174+
func(ctx context.Context, req *mcp.CallToolRequest, args PostCommentArgs) (*mcp.CallToolResult, any, error) {
175+
argsMap := map[string]interface{}{
176+
"feed_id": args.FeedID,
177+
"xsec_token": args.XsecToken,
178+
"content": args.Content,
179+
}
180+
result := appServer.handlePostComment(ctx, argsMap)
181+
return convertToMCPResult(result), nil, nil
182+
},
183+
)
184+
185+
logrus.Infof("Registered %d MCP tools", 8)
186+
}
187+
188+
// convertToMCPResult 将自定义的 MCPToolResult 转换为官方 SDK 的格式
189+
func convertToMCPResult(result *MCPToolResult) *mcp.CallToolResult {
190+
var contents []mcp.Content
191+
for _, c := range result.Content {
192+
switch c.Type {
193+
case "text":
194+
contents = append(contents, &mcp.TextContent{Text: c.Text})
195+
case "image":
196+
// 解码 base64 字符串为 []byte
197+
imageData, err := base64.StdEncoding.DecodeString(c.Data)
198+
if err != nil {
199+
logrus.WithError(err).Error("Failed to decode base64 image data")
200+
// 如果解码失败,添加错误文本
201+
contents = append(contents, &mcp.TextContent{
202+
Text: "图片数据解码失败: " + err.Error(),
203+
})
204+
} else {
205+
contents = append(contents, &mcp.ImageContent{
206+
Data: imageData,
207+
MIMEType: c.MimeType,
208+
})
209+
}
210+
}
211+
}
212+
213+
return &mcp.CallToolResult{
214+
Content: contents,
215+
IsError: result.IsError,
216+
}
217+
}
218+
219+
// convertStringsToInterfaces 辅助函数:将 []string 转换为 []interface{}
220+
func convertStringsToInterfaces(strs []string) []interface{} {
221+
result := make([]interface{}, len(strs))
222+
for i, s := range strs {
223+
result[i] = s
224+
}
225+
return result
226+
}

0 commit comments

Comments
 (0)