@@ -3,6 +3,8 @@ package main
33import (
44 "context"
55 "encoding/base64"
6+ "fmt"
7+ "runtime/debug"
68
79 "github.com/modelcontextprotocol/go-sdk/mcp"
810 "github.com/sirupsen/logrus"
@@ -89,6 +91,38 @@ func InitMCPServer(appServer *AppServer) *mcp.Server {
8991 return server
9092}
9193
94+ func withPanicRecovery [T any ](
95+ toolName string ,
96+ handler func (context.Context , * mcp.CallToolRequest , T ) (* mcp.CallToolResult , any , error ),
97+ ) func (context.Context , * mcp.CallToolRequest , T ) (* mcp.CallToolResult , any , error ) {
98+
99+ return func (ctx context.Context , req * mcp.CallToolRequest , args T ) (result * mcp.CallToolResult , resp any , err error ) {
100+ defer func () {
101+ if r := recover (); r != nil {
102+ logrus .WithFields (logrus.Fields {
103+ "tool" : toolName ,
104+ "panic" : r ,
105+ }).Error ("Tool handler panicked" )
106+
107+ logrus .Errorf ("Stack trace:\n %s" , debug .Stack ())
108+
109+ result = & mcp.CallToolResult {
110+ Content : []mcp.Content {
111+ & mcp.TextContent {
112+ Text : fmt .Sprintf ("工具 %s 执行时发生内部错误: %v\n \n 请查看服务端日志获取详细信息。" , toolName , r ),
113+ },
114+ },
115+ IsError : true ,
116+ }
117+ resp = nil
118+ err = nil
119+ }
120+ }()
121+
122+ return handler (ctx , req , args )
123+ }
124+ }
125+
92126// registerTools 注册所有 MCP 工具
93127func registerTools (server * mcp.Server , appServer * AppServer ) {
94128 // 工具 1: 检查登录状态
@@ -97,10 +131,10 @@ func registerTools(server *mcp.Server, appServer *AppServer) {
97131 Name : "check_login_status" ,
98132 Description : "检查小红书登录状态" ,
99133 },
100- func (ctx context.Context , req * mcp.CallToolRequest , _ any ) (* mcp.CallToolResult , any , error ) {
134+ withPanicRecovery ( "check_login_status" , func (ctx context.Context , req * mcp.CallToolRequest , _ any ) (* mcp.CallToolResult , any , error ) {
101135 result := appServer .handleCheckLoginStatus (ctx )
102136 return convertToMCPResult (result ), nil , nil
103- },
137+ }) ,
104138 )
105139
106140 // 工具 2: 获取登录二维码
@@ -109,10 +143,10 @@ func registerTools(server *mcp.Server, appServer *AppServer) {
109143 Name : "get_login_qrcode" ,
110144 Description : "获取登录二维码(返回 Base64 图片和超时时间)" ,
111145 },
112- func (ctx context.Context , req * mcp.CallToolRequest , _ any ) (* mcp.CallToolResult , any , error ) {
146+ withPanicRecovery ( "get_login_qrcode" , func (ctx context.Context , req * mcp.CallToolRequest , _ any ) (* mcp.CallToolResult , any , error ) {
113147 result := appServer .handleGetLoginQrcode (ctx )
114148 return convertToMCPResult (result ), nil , nil
115- },
149+ }) ,
116150 )
117151
118152 // 工具 3: 发布内容
@@ -121,7 +155,7 @@ func registerTools(server *mcp.Server, appServer *AppServer) {
121155 Name : "publish_content" ,
122156 Description : "发布小红书图文内容" ,
123157 },
124- func (ctx context.Context , req * mcp.CallToolRequest , args PublishContentArgs ) (* mcp.CallToolResult , any , error ) {
158+ withPanicRecovery ( "publish_content" , func (ctx context.Context , req * mcp.CallToolRequest , args PublishContentArgs ) (* mcp.CallToolResult , any , error ) {
125159 // 转换参数格式到现有的 handler
126160 argsMap := map [string ]interface {}{
127161 "title" : args .Title ,
@@ -131,19 +165,19 @@ func registerTools(server *mcp.Server, appServer *AppServer) {
131165 }
132166 result := appServer .handlePublishContent (ctx , argsMap )
133167 return convertToMCPResult (result ), nil , nil
134- },
168+ }) ,
135169 )
136170
137171 // 工具 4: 获取Feed列表
138172 mcp .AddTool (server ,
139173 & mcp.Tool {
140174 Name : "list_feeds" ,
141- Description : "获取用户发布的内容列表 " ,
175+ Description : "获取首页 Feeds 列表 " ,
142176 },
143- func (ctx context.Context , req * mcp.CallToolRequest , _ any ) (* mcp.CallToolResult , any , error ) {
177+ withPanicRecovery ( "list_feeds" , func (ctx context.Context , req * mcp.CallToolRequest , _ any ) (* mcp.CallToolResult , any , error ) {
144178 result := appServer .handleListFeeds (ctx )
145179 return convertToMCPResult (result ), nil , nil
146- },
180+ }) ,
147181 )
148182
149183 // 工具 5: 搜索内容
@@ -152,10 +186,10 @@ func registerTools(server *mcp.Server, appServer *AppServer) {
152186 Name : "search_feeds" ,
153187 Description : "搜索小红书内容(需要已登录)" ,
154188 },
155- func (ctx context.Context , req * mcp.CallToolRequest , args SearchFeedsArgs ) (* mcp.CallToolResult , any , error ) {
189+ withPanicRecovery ( "search_feeds" , func (ctx context.Context , req * mcp.CallToolRequest , args SearchFeedsArgs ) (* mcp.CallToolResult , any , error ) {
156190 result := appServer .handleSearchFeeds (ctx , args )
157191 return convertToMCPResult (result ), nil , nil
158- },
192+ }) ,
159193 )
160194
161195 // 工具 6: 获取Feed详情
@@ -164,14 +198,14 @@ func registerTools(server *mcp.Server, appServer *AppServer) {
164198 Name : "get_feed_detail" ,
165199 Description : "获取小红书笔记详情,返回笔记内容、图片、作者信息、互动数据(点赞/收藏/分享数)及评论列表" ,
166200 },
167- func (ctx context.Context , req * mcp.CallToolRequest , args FeedDetailArgs ) (* mcp.CallToolResult , any , error ) {
201+ withPanicRecovery ( "get_feed_detail" , func (ctx context.Context , req * mcp.CallToolRequest , args FeedDetailArgs ) (* mcp.CallToolResult , any , error ) {
168202 argsMap := map [string ]interface {}{
169203 "feed_id" : args .FeedID ,
170204 "xsec_token" : args .XsecToken ,
171205 }
172206 result := appServer .handleGetFeedDetail (ctx , argsMap )
173207 return convertToMCPResult (result ), nil , nil
174- },
208+ }) ,
175209 )
176210
177211 // 工具 7: 获取用户主页
@@ -180,14 +214,14 @@ func registerTools(server *mcp.Server, appServer *AppServer) {
180214 Name : "user_profile" ,
181215 Description : "获取指定的小红书用户主页,返回用户基本信息,关注、粉丝、获赞量及其笔记内容" ,
182216 },
183- func (ctx context.Context , req * mcp.CallToolRequest , args UserProfileArgs ) (* mcp.CallToolResult , any , error ) {
217+ withPanicRecovery ( "user_profile" , func (ctx context.Context , req * mcp.CallToolRequest , args UserProfileArgs ) (* mcp.CallToolResult , any , error ) {
184218 argsMap := map [string ]interface {}{
185219 "user_id" : args .UserID ,
186220 "xsec_token" : args .XsecToken ,
187221 }
188222 result := appServer .handleUserProfile (ctx , argsMap )
189223 return convertToMCPResult (result ), nil , nil
190- },
224+ }) ,
191225 )
192226
193227 // 工具 8: 发表评论
@@ -196,15 +230,15 @@ func registerTools(server *mcp.Server, appServer *AppServer) {
196230 Name : "post_comment_to_feed" ,
197231 Description : "发表评论到小红书笔记" ,
198232 },
199- func (ctx context.Context , req * mcp.CallToolRequest , args PostCommentArgs ) (* mcp.CallToolResult , any , error ) {
233+ withPanicRecovery ( "post_comment_to_feed" , func (ctx context.Context , req * mcp.CallToolRequest , args PostCommentArgs ) (* mcp.CallToolResult , any , error ) {
200234 argsMap := map [string ]interface {}{
201235 "feed_id" : args .FeedID ,
202236 "xsec_token" : args .XsecToken ,
203237 "content" : args .Content ,
204238 }
205239 result := appServer .handlePostComment (ctx , argsMap )
206240 return convertToMCPResult (result ), nil , nil
207- },
241+ }) ,
208242 )
209243
210244 // 工具 9: 发布视频(仅本地文件)
@@ -213,7 +247,7 @@ func registerTools(server *mcp.Server, appServer *AppServer) {
213247 Name : "publish_with_video" ,
214248 Description : "发布小红书视频内容(仅支持本地单个视频文件)" ,
215249 },
216- func (ctx context.Context , req * mcp.CallToolRequest , args PublishVideoArgs ) (* mcp.CallToolResult , any , error ) {
250+ withPanicRecovery ( "publish_with_video" , func (ctx context.Context , req * mcp.CallToolRequest , args PublishVideoArgs ) (* mcp.CallToolResult , any , error ) {
217251 argsMap := map [string ]interface {}{
218252 "title" : args .Title ,
219253 "content" : args .Content ,
@@ -222,7 +256,7 @@ func registerTools(server *mcp.Server, appServer *AppServer) {
222256 }
223257 result := appServer .handlePublishVideo (ctx , argsMap )
224258 return convertToMCPResult (result ), nil , nil
225- },
259+ }) ,
226260 )
227261
228262 // 工具 10: 点赞笔记
@@ -231,15 +265,15 @@ func registerTools(server *mcp.Server, appServer *AppServer) {
231265 Name : "like_feed" ,
232266 Description : "为指定笔记点赞或取消点赞(如已点赞将跳过点赞,如未点赞将跳过取消点赞)" ,
233267 },
234- func (ctx context.Context , req * mcp.CallToolRequest , args LikeFeedArgs ) (* mcp.CallToolResult , any , error ) {
268+ withPanicRecovery ( "like_feed" , func (ctx context.Context , req * mcp.CallToolRequest , args LikeFeedArgs ) (* mcp.CallToolResult , any , error ) {
235269 argsMap := map [string ]interface {}{
236270 "feed_id" : args .FeedID ,
237271 "xsec_token" : args .XsecToken ,
238272 "unlike" : args .Unlike ,
239273 }
240274 result := appServer .handleLikeFeed (ctx , argsMap )
241275 return convertToMCPResult (result ), nil , nil
242- },
276+ }) ,
243277 )
244278
245279 // 工具 11: 收藏笔记
@@ -248,15 +282,15 @@ func registerTools(server *mcp.Server, appServer *AppServer) {
248282 Name : "favorite_feed" ,
249283 Description : "收藏指定笔记或取消收藏(如已收藏将跳过收藏,如未收藏将跳过取消收藏)" ,
250284 },
251- func (ctx context.Context , req * mcp.CallToolRequest , args FavoriteFeedArgs ) (* mcp.CallToolResult , any , error ) {
285+ withPanicRecovery ( "favorite_feed" , func (ctx context.Context , req * mcp.CallToolRequest , args FavoriteFeedArgs ) (* mcp.CallToolResult , any , error ) {
252286 argsMap := map [string ]interface {}{
253287 "feed_id" : args .FeedID ,
254288 "xsec_token" : args .XsecToken ,
255289 "unfavorite" : args .Unfavorite ,
256290 }
257291 result := appServer .handleFavoriteFeed (ctx , argsMap )
258292 return convertToMCPResult (result ), nil , nil
259- },
293+ }) ,
260294 )
261295
262296 logrus .Infof ("Registered %d MCP tools" , 11 )
0 commit comments