* feat: add feed detail page functionality with gin and MCP interfaces Add comprehensive Feed detail page support: - Create new FeedDetailAction in xiaohongshu/feed_detail.go - Add HTTP API endpoint POST /api/v1/feeds/detail - Add MCP tool 'get_feed_detail' for MCP protocol support - Support feed_id and xsec_token parameters (both required) - Raw __INITIAL_STATE__ JSON data saved to feed_detail.json - Return structured data for both HTTP and MCP interfaces 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> * feat: enhance feed detail functionality with MCP interface improvements - Add comprehensive feed detail page support with proper data extraction - Create dedicated feed_detail.go file for FeedDetailAction - Optimize Go struct definitions based on actual JSON data analysis - Remove unnecessary fields from FeedDetail, DetailImageInfo, CommentList, and Comment structs - Update MCP interface description to reflect comment retrieval capability - Support both HTTP REST API and MCP protocol interfaces - Implement proper Vue 3 reactive data extraction from window.__INITIAL_STATE__ - Include feed content, user info, interaction data, and comment lists 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> * fix: restore JSON file writing for testing and improve code structure - Restore feed_detail.json file writing for testing purposes - Improve error handling by separating marshal and unmarshal steps - Keep the original data extraction logic for complex Vue reactive data structure 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> * refactor: simplify JSON unmarshaling using struct instead of map[string]any - Replace complex map[string]any extraction with direct struct unmarshaling - Define inline struct matching the actual JSON response structure - Remove unnecessary extractFeedDetailData and extractNestedValue methods - Significantly reduce code complexity and improve readability 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> * docs: improve MCP interface descriptions for better usability - Enhance get_feed_detail parameter descriptions with clear source information - Clarify publish_content images parameter supports both local paths and URLs - Improve search_feeds description to specify supported search types - Keep descriptions concise and practical without over-complication 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> * fix: revert search_feeds keyword description to keep it simple - Remove unnecessary details from keyword description - Keep interface descriptions concise and clear 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com>
226 lines
5.0 KiB
Go
226 lines
5.0 KiB
Go
package main
|
||
|
||
import (
|
||
"context"
|
||
"encoding/json"
|
||
"fmt"
|
||
|
||
"github.com/sirupsen/logrus"
|
||
)
|
||
|
||
// MCP 工具处理函数
|
||
|
||
// handleCheckLoginStatus 处理检查登录状态
|
||
func (s *AppServer) handleCheckLoginStatus(ctx context.Context) *MCPToolResult {
|
||
logrus.Info("MCP: 检查登录状态")
|
||
|
||
status, err := s.xiaohongshuService.CheckLoginStatus(ctx)
|
||
if err != nil {
|
||
return &MCPToolResult{
|
||
Content: []MCPContent{{
|
||
Type: "text",
|
||
Text: "检查登录状态失败: " + err.Error(),
|
||
}},
|
||
IsError: true,
|
||
}
|
||
}
|
||
|
||
resultText := fmt.Sprintf("登录状态检查成功: %+v", status)
|
||
return &MCPToolResult{
|
||
Content: []MCPContent{{
|
||
Type: "text",
|
||
Text: resultText,
|
||
}},
|
||
}
|
||
}
|
||
|
||
// handlePublishContent 处理发布内容
|
||
func (s *AppServer) handlePublishContent(ctx context.Context, args map[string]interface{}) *MCPToolResult {
|
||
logrus.Info("MCP: 发布内容")
|
||
|
||
// 解析参数
|
||
title, _ := args["title"].(string)
|
||
content, _ := args["content"].(string)
|
||
imagePathsInterface, _ := args["images"].([]interface{})
|
||
|
||
var imagePaths []string
|
||
for _, path := range imagePathsInterface {
|
||
if pathStr, ok := path.(string); ok {
|
||
imagePaths = append(imagePaths, pathStr)
|
||
}
|
||
}
|
||
|
||
logrus.Infof("MCP: 发布内容 - 标题: %s, 图片数量: %d", title, len(imagePaths))
|
||
|
||
// 构建发布请求
|
||
req := &PublishRequest{
|
||
Title: title,
|
||
Content: content,
|
||
Images: imagePaths,
|
||
}
|
||
|
||
// 执行发布
|
||
result, err := s.xiaohongshuService.PublishContent(ctx, req)
|
||
if err != nil {
|
||
return &MCPToolResult{
|
||
Content: []MCPContent{{
|
||
Type: "text",
|
||
Text: "发布失败: " + err.Error(),
|
||
}},
|
||
IsError: true,
|
||
}
|
||
}
|
||
|
||
resultText := fmt.Sprintf("内容发布成功: %+v", result)
|
||
return &MCPToolResult{
|
||
Content: []MCPContent{{
|
||
Type: "text",
|
||
Text: resultText,
|
||
}},
|
||
}
|
||
}
|
||
|
||
// handleListFeeds 处理获取Feeds列表
|
||
func (s *AppServer) handleListFeeds(ctx context.Context) *MCPToolResult {
|
||
logrus.Info("MCP: 获取Feeds列表")
|
||
|
||
result, err := s.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),
|
||
}},
|
||
}
|
||
}
|
||
|
||
// handleSearchFeeds 处理搜索Feeds
|
||
func (s *AppServer) handleSearchFeeds(ctx context.Context, args map[string]interface{}) *MCPToolResult {
|
||
logrus.Info("MCP: 搜索Feeds")
|
||
|
||
// 解析参数
|
||
keyword, ok := args["keyword"].(string)
|
||
if !ok || keyword == "" {
|
||
return &MCPToolResult{
|
||
Content: []MCPContent{{
|
||
Type: "text",
|
||
Text: "搜索Feeds失败: 缺少关键词参数",
|
||
}},
|
||
IsError: true,
|
||
}
|
||
}
|
||
|
||
logrus.Infof("MCP: 搜索Feeds - 关键词: %s", keyword)
|
||
|
||
result, err := s.xiaohongshuService.SearchFeeds(ctx, keyword)
|
||
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),
|
||
}},
|
||
}
|
||
}
|
||
|
||
// handleGetFeedDetail 处理获取Feed详情
|
||
func (s *AppServer) handleGetFeedDetail(ctx context.Context, args map[string]any) *MCPToolResult {
|
||
logrus.Info("MCP: 获取Feed详情")
|
||
|
||
// 解析参数
|
||
feedID, ok := args["feed_id"].(string)
|
||
if !ok || feedID == "" {
|
||
return &MCPToolResult{
|
||
Content: []MCPContent{{
|
||
Type: "text",
|
||
Text: "获取Feed详情失败: 缺少feed_id参数",
|
||
}},
|
||
IsError: true,
|
||
}
|
||
}
|
||
|
||
xsecToken, ok := args["xsec_token"].(string)
|
||
if !ok || xsecToken == "" {
|
||
return &MCPToolResult{
|
||
Content: []MCPContent{{
|
||
Type: "text",
|
||
Text: "获取Feed详情失败: 缺少xsec_token参数",
|
||
}},
|
||
IsError: true,
|
||
}
|
||
}
|
||
|
||
logrus.Infof("MCP: 获取Feed详情 - Feed ID: %s", feedID)
|
||
|
||
result, err := s.xiaohongshuService.GetFeedDetail(ctx, feedID, xsecToken)
|
||
if err != nil {
|
||
return &MCPToolResult{
|
||
Content: []MCPContent{{
|
||
Type: "text",
|
||
Text: "获取Feed详情失败: " + err.Error(),
|
||
}},
|
||
IsError: true,
|
||
}
|
||
}
|
||
|
||
// 格式化输出,转换为JSON字符串
|
||
jsonData, err := json.MarshalIndent(result, "", " ")
|
||
if err != nil {
|
||
return &MCPToolResult{
|
||
Content: []MCPContent{{
|
||
Type: "text",
|
||
Text: fmt.Sprintf("获取Feed详情成功,但序列化失败: %v", err),
|
||
}},
|
||
IsError: true,
|
||
}
|
||
}
|
||
|
||
return &MCPToolResult{
|
||
Content: []MCPContent{{
|
||
Type: "text",
|
||
Text: string(jsonData),
|
||
}},
|
||
}
|
||
}
|