feat: 添加获取小红书首页 Feeds 列表的 HTTP 和 MCP 接口
- 在 service.go 中添加 ListFeeds 业务逻辑,复用 xiaohongshu 包功能 - 添加 HTTP 接口 GET /api/v1/feeds/list - 添加 MCP tool: list_feeds,支持通过 MCP 协议获取 Feeds - 返回结构化的 Feeds 数据,包含列表和数量统计 - 更新 .gitignore 忽略构建产物和测试脚本 - 更新项目配置,添加 chmod 权限 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -11,7 +11,8 @@
|
|||||||
"Bash(rmdir:*)",
|
"Bash(rmdir:*)",
|
||||||
"Bash(rm:*)",
|
"Bash(rm:*)",
|
||||||
"Bash(gofmt:*)",
|
"Bash(gofmt:*)",
|
||||||
"Bash(goimports:*)"
|
"Bash(goimports:*)",
|
||||||
|
"Bash(chmod:*)"
|
||||||
],
|
],
|
||||||
"deny": []
|
"deny": []
|
||||||
}
|
}
|
||||||
|
|||||||
6
.gitignore
vendored
6
.gitignore
vendored
@@ -31,3 +31,9 @@ go.work.sum
|
|||||||
# .idea/
|
# .idea/
|
||||||
# .vscode/
|
# .vscode/
|
||||||
# .claude/
|
# .claude/
|
||||||
|
|
||||||
|
# Build artifacts
|
||||||
|
xiaohongshu-mcp
|
||||||
|
|
||||||
|
# Test scripts
|
||||||
|
test_*.sh
|
||||||
|
|||||||
@@ -1 +1,2 @@
|
|||||||
- 要求每次修改完后,需要帮我格式化 Go 源码文件.
|
- 要求每次修改完后,需要帮我格式化 Go 源码文件.
|
||||||
|
- 测试过程中产生的脚本和build中间文件,如果没有必要,则删除.
|
||||||
14
handlers.go
14
handlers.go
@@ -90,6 +90,20 @@ func publishHandler(c *gin.Context) {
|
|||||||
respondSuccess(c, result, "发布成功")
|
respondSuccess(c, result, "发布成功")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// listFeedsHandler 获取Feeds列表
|
||||||
|
func listFeedsHandler(c *gin.Context) {
|
||||||
|
// 获取 Feeds 列表
|
||||||
|
result, err := xiaohongshuService.ListFeeds(c.Request.Context())
|
||||||
|
if err != nil {
|
||||||
|
respondError(c, http.StatusInternalServerError, "LIST_FEEDS_FAILED",
|
||||||
|
"获取Feeds列表失败", err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Set("account", "ai-report")
|
||||||
|
respondSuccess(c, result, "获取Feeds列表成功")
|
||||||
|
}
|
||||||
|
|
||||||
// healthHandler 健康检查
|
// healthHandler 健康检查
|
||||||
func healthHandler(c *gin.Context) {
|
func healthHandler(c *gin.Context) {
|
||||||
respondSuccess(c, map[string]any{
|
respondSuccess(c, map[string]any{
|
||||||
|
|||||||
@@ -118,6 +118,41 @@ func handlePublishContent(ctx context.Context, args map[string]interface{}) *MCP
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// handleListFeeds 处理获取Feeds列表
|
||||||
|
func handleListFeeds(ctx context.Context) *MCPToolResult {
|
||||||
|
logrus.Info("MCP: 获取Feeds列表")
|
||||||
|
|
||||||
|
result, err := xiaohongshuService.ListFeeds(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return &MCPToolResult{
|
||||||
|
Content: []MCPContent{{
|
||||||
|
Type: "text",
|
||||||
|
Text: "获取Feeds列表失败: " + err.Error(),
|
||||||
|
}},
|
||||||
|
IsError: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 格式化输出,转换为JSON字符串
|
||||||
|
jsonData, err := json.MarshalIndent(result, "", " ")
|
||||||
|
if err != nil {
|
||||||
|
return &MCPToolResult{
|
||||||
|
Content: []MCPContent{{
|
||||||
|
Type: "text",
|
||||||
|
Text: fmt.Sprintf("获取Feeds列表成功,但序列化失败: %v", err),
|
||||||
|
}},
|
||||||
|
IsError: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &MCPToolResult{
|
||||||
|
Content: []MCPContent{{
|
||||||
|
Type: "text",
|
||||||
|
Text: string(jsonData),
|
||||||
|
}},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// handleMCPRequest 处理 MCP 请求
|
// handleMCPRequest 处理 MCP 请求
|
||||||
func handleMCPRequest(w http.ResponseWriter, r *http.Request) {
|
func handleMCPRequest(w http.ResponseWriter, r *http.Request) {
|
||||||
var req JSONRPCRequest
|
var req JSONRPCRequest
|
||||||
@@ -193,6 +228,14 @@ func handleToolsList(w http.ResponseWriter, req JSONRPCRequest) {
|
|||||||
"required": []string{"title", "content", "images"},
|
"required": []string{"title", "content", "images"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "list_feeds",
|
||||||
|
"description": "获取小红书首页Feeds列表",
|
||||||
|
"inputSchema": map[string]interface{}{
|
||||||
|
"type": "object",
|
||||||
|
"properties": map[string]interface{}{},
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
result := map[string]interface{}{
|
result := map[string]interface{}{
|
||||||
@@ -220,6 +263,8 @@ func handleToolsCall(w http.ResponseWriter, r *http.Request, req JSONRPCRequest)
|
|||||||
result = handleCheckLoginStatus(ctx)
|
result = handleCheckLoginStatus(ctx)
|
||||||
case "publish_content":
|
case "publish_content":
|
||||||
result = handlePublishContent(ctx, toolCall.Arguments)
|
result = handlePublishContent(ctx, toolCall.Arguments)
|
||||||
|
case "list_feeds":
|
||||||
|
result = handleListFeeds(ctx)
|
||||||
default:
|
default:
|
||||||
logrus.Warnf("不支持的工具: %s", toolCall.Name)
|
logrus.Warnf("不支持的工具: %s", toolCall.Name)
|
||||||
sendJSONRPCError(w, req.ID, -32601, "Tool not found", nil)
|
sendJSONRPCError(w, req.ID, -32601, "Tool not found", nil)
|
||||||
|
|||||||
@@ -40,6 +40,9 @@ func setupRouter() *gin.Engine {
|
|||||||
api.GET("/login/status", checkLoginStatusHandler)
|
api.GET("/login/status", checkLoginStatusHandler)
|
||||||
|
|
||||||
api.POST("/publish", publishHandler)
|
api.POST("/publish", publishHandler)
|
||||||
|
|
||||||
|
// Feeds 相关路由
|
||||||
|
api.GET("/feeds/list", listFeedsHandler)
|
||||||
}
|
}
|
||||||
|
|
||||||
return router
|
return router
|
||||||
|
|||||||
29
service.go
29
service.go
@@ -39,6 +39,12 @@ type PublishResponse struct {
|
|||||||
PostID string `json:"post_id,omitempty"`
|
PostID string `json:"post_id,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FeedsListResponse Feeds列表响应
|
||||||
|
type FeedsListResponse struct {
|
||||||
|
Feeds []xiaohongshu.Feed `json:"feeds"`
|
||||||
|
Count int `json:"count"`
|
||||||
|
}
|
||||||
|
|
||||||
// CheckLoginStatus 检查登录状态
|
// CheckLoginStatus 检查登录状态
|
||||||
func (s *XiaohongshuService) CheckLoginStatus(ctx context.Context) (*LoginStatusResponse, error) {
|
func (s *XiaohongshuService) CheckLoginStatus(ctx context.Context) (*LoginStatusResponse, error) {
|
||||||
// 使用全局单例浏览器创建新页面
|
// 使用全局单例浏览器创建新页面
|
||||||
@@ -109,3 +115,26 @@ func (s *XiaohongshuService) publishContent(ctx context.Context, content xiaohon
|
|||||||
// 执行发布
|
// 执行发布
|
||||||
return action.Publish(ctx, content)
|
return action.Publish(ctx, content)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ListFeeds 获取Feeds列表
|
||||||
|
func (s *XiaohongshuService) ListFeeds(ctx context.Context) (*FeedsListResponse, error) {
|
||||||
|
// 使用全局单例浏览器创建新页面
|
||||||
|
page := browser.NewPage()
|
||||||
|
defer page.Close()
|
||||||
|
|
||||||
|
// 创建 Feeds 列表 action
|
||||||
|
action := xiaohongshu.NewFeedsListAction(page)
|
||||||
|
|
||||||
|
// 获取 Feeds 列表
|
||||||
|
feeds, err := action.GetFeedsList(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
response := &FeedsListResponse{
|
||||||
|
Feeds: feeds,
|
||||||
|
Count: len(feeds),
|
||||||
|
}
|
||||||
|
|
||||||
|
return response, nil
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user