Skip to content

feat: add resolve_shortlink tool to extract feed_id and xsec_token from xhslink.com URLs#589

Open
zero469 wants to merge 2 commits intoxpzouying:mainfrom
zero469:feature/resolve-shortlink
Open

feat: add resolve_shortlink tool to extract feed_id and xsec_token from xhslink.com URLs#589
zero469 wants to merge 2 commits intoxpzouying:mainfrom
zero469:feature/resolve-shortlink

Conversation

@zero469
Copy link

@zero469 zero469 commented Mar 18, 2026

Summary

  • 新增 resolve_shortlink MCP 工具,可从小红书分享短链接(xhslink.com)直接提取 feed_idxsec_token
  • 无需先搜索即可直接获取笔记详情,简化 AI 工作流程

Changes

  • xiaohongshu/shortlink.go - 新增短链接解析核心逻辑,支持 HTTP 重定向跟踪和 URL 参数提取
  • service.go - 添加 ResolveShortLink 服务方法
  • mcp_server.go - 注册第 14 个 MCP 工具 resolve_shortlink
  • mcp_handlers.go - 添加 handleResolveShortLink 处理函数

Usage

输入:http://xhslink.com/o/1kZVDhZks0n

输出:

{
  "feed_id": "69ba1706000000001a023daf",
  "xsec_token": "CBc35g8tRNZgaxC5icq1yawooXNOzVHw8ke6PGldcYa8U=",
  "original_url": "https://xhslink.com/o/1kZVDhZks0n",
  "redirect_url": "https://www.xiaohongshu.com/discovery/item/...",
  "share_id": "f5d4aabc8e014cce9d331b372eb94a22",
  "app_platform": "ios",
  "xsec_source": "app_share"
}

Testing

  • go build 编译通过
  • ✅ 单元测试通过(URL 解析逻辑)
  • ✅ 端到端测试通过(真实短链接验证)

…om xhslink.com URLs

- Add xiaohongshu/shortlink.go with HTTP redirect-based URL resolution
- Uses mobile User-Agent to get proper 302 redirect from xiaohongshu
- Extracts feed_id from path and xsec_token from query params
- Register as MCP tool xpzouying#14 for direct short link parsing
@zero469 zero469 requested a review from xpzouying as a code owner March 18, 2026 15:42
@kimi-agent-bot
Copy link
Contributor

🔍 Kimi Code Agent 正在自动审查此 PR...

Copy link
Contributor

@kimi-agent-bot kimi-agent-bot bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

代码审查意见 — PR #589

📋 概述

本 PR 新增 resolve_shortlink MCP 工具,通过 HTTP 302 重定向解析小红书分享短链接(xhslink.com),提取 feed_idxsec_token。功能定义清晰,实现思路合理,PR 描述完善。以下是具体的审查意见。


✅ 亮点

  1. 良好的关注点分离 — 核心解析逻辑放在 xiaohongshu/shortlink.go,handler / service / server 注册各司其职,符合项目分层架构。
  2. 安全的重定向处理 — 使用 CheckRedirect 禁止自动跟随重定向,手动提取 Location header,避免不必要的请求泄露。
  3. 域名白名单校验normalizeShortLinkURL 对输入域名做了校验,防止 SSRF 风险。
  4. PR 描述 — 包含 Summary / Changes / Usage / Testing 各项,信息充分。

🔍 需改进的问题

1. 🔴 正则在函数内重复编译(性能问题)

extractFeedIDFromPath 每次调用都会 regexp.MustCompile 编译 3 个正则表达式。虽然项目中其他地方也有类似写法,但这是一个已知的反模式。建议提升到包级变量:

var (
    feedIDPatterns = []*regexp.Regexp{
        regexp.MustCompile(`/discovery/item/([a-zA-Z0-9]+)`),
        regexp.MustCompile(`/explore/([a-zA-Z0-9]+)`),
        regexp.MustCompile(`/note/([a-zA-Z0-9]+)`),
    }
)

2. 🔴 Handler 函数签名与项目主流模式不一致

项目中大多数 handler 接收 map[string]interface{} 或 typed struct args,例如 handleSearchFeeds(ctx, args SearchFeedsArgs)。而此 PR 中的 handleResolveShortLink 接收 string

func (s *AppServer) handleResolveShortLink(ctx context.Context, shortURL string) *MCPToolResult

这意味着参数提取和验证被拆分到两个地方(handler 校验空字符串,但 MCP server 层拆解 args.URL)。建议统一为:

func (s *AppServer) handleResolveShortLink(ctx context.Context, args ResolveShortLinkArgs) *MCPToolResult {
    if args.URL == "" {
        // ...
    }
    // ...
}

这样也与 handleSearchFeeds 的 typed struct 模式一致。

3. 🟡 xsec_token 被打印到日志中(安全隐患)

logrus.Infof("解析成功: feed_id=%s, xsec_token=%s", feedID, xsecToken)

xsec_token 是一个令牌类参数,完整打印在日志中可能存在安全风险。建议移除或脱敏处理:

logrus.Infof("解析成功: feed_id=%s", feedID)

4. 🟡 缺少单元测试

PR 描述中提到"单元测试通过"和"端到端测试通过",但 diff 中没有包含任何 *_test.go 文件。normalizeShortLinkURLparseRedirectURLextractFeedIDFromPath 都是纯函数,非常适合编写表驱动单元测试。建议至少补充 shortlink_test.go

5. 🟡 HTTP Client 未复用

每次调用 ResolveShortLink 都会新建一个 http.Client。虽然 Go 的 http.Client 创建成本不高,但内部的 Transport 连接池无法跨请求复用。如果短时间内频繁调用,建议将 client 提升为包级变量或注入到 service 中:

var shortLinkClient = &http.Client{
    Timeout: 10 * time.Second,
    CheckRedirect: func(req *http.Request, via []*http.Request) error {
        return http.ErrUseLastResponse
    },
}

6. 🟢 comment_feed.go 的空白字符修改

xiaohongshu/comment_feed.go 的修改仅为行尾空白清理(trailing whitespace),不影响功能。建议在单独的 commit 中处理此类格式化修改,保持功能 PR 的 diff 干净。


💡 其他建议(非阻塞)

  • 支持更多状态码: 除 301/302 外,小红书可能返回 307/308 重定向,可考虑扩展 resp.StatusCode 的判断范围。
  • 超时可配置: 当前硬编码 10 * time.Second,可考虑从 context 或配置中获取。
  • 输入容错: 用户可能粘贴包含中文字符或额外文本的链接(如 「小红书笔记」https://xhslink.com/xxx),可在 normalizeShortLinkURL 中增加 URL 提取逻辑。

📊 总结

维度 评价
功能完整性 ⭐⭐⭐⭐ 功能清晰完整
代码质量 ⭐⭐⭐ 整体良好,有几处可改进
安全性 ⭐⭐⭐ 有域名白名单,但 token 日志需处理
测试 ⭐⭐ 缺少测试文件
一致性 ⭐⭐⭐ 基本遵循项目模式,handler 签名需对齐

建议: 请解决 🔴 标记的问题后合并。🟡 问题建议修复但非阻塞。


🤖 由 Kimi Code Agent 自动审查

@kimi-agent-bot
Copy link
Contributor

Now let me look at the existing code patterns to understand the codebase conventions:
Explore the repo xpzouying/xiaohongshu-mcp to understand:

  1. Does the repo have any existing tests (look for *_test.go files)?
  2. What patterns are used in other handler functions in mcp_handlers.go? Specifically how do they validate arguments and call services?
  3. Look at the existing xiaohongshu/ package to see what patterns they follow (e.g., do they compile regexes at package level vs. inside functions?)
  4. Check if there are any existing http clients or helpers in the codebase that are reused.
  5. What does the service.go look like - do service methods typically do more than just delegate?

Be thorough and report back specific code examples.
Now I have a thorough understanding of the codebase patterns and the PR changes. Let me compose the review:
已完成对 PR #589 的代码审查,评论已发布到 GitHub PR 页面。以下是审查要点摘要:

主要发现

🔴 需修复(2 项):

  1. 正则在函数内重复编译extractFeedIDFromPath 每次调用都编译 3 个正则,应提升为包级变量
  2. Handler 签名不一致handleResolveShortLink 直接接收 string 参数,而非项目中常用的 typed struct 模式,建议统一

🟡 建议改进(3 项):
3. xsec_token 完整打印到日志 — 存在安全隐患,建议脱敏
4. 缺少单元测试文件 — PR 描述提及测试通过,但 diff 中无 *_test.go 文件
5. HTTP Client 未复用 — 每次调用新建 client,连接池无法复用

🟢 非阻塞:
6. comment_feed.go 的空白字符清理建议独立 commit

- Move regex patterns to package-level vars (avoid recompilation)
- Reuse HTTP client with connection pool
- Handler now accepts ResolveShortLinkArgs struct (consistent with other handlers)
- Remove xsec_token from logs (security)
@zero469
Copy link
Author

zero469 commented Mar 18, 2026

感谢 @kimi-agent-bot 的详细审查!已在最新 commit 中解决所有问题:

🔴 已修复

  1. 正则在函数内重复编译 — 已提升为包级变量 feedIDPatterns
  2. Handler 签名不一致 — 已改为 handleResolveShortLink(ctx, args ResolveShortLinkArgs),与项目其他 handler 保持一致

🟡 已修复

  1. xsec_token 打印到日志 — 已移除,仅保留 feed_id
  2. HTTP Client 未复用 — 已提升为包级变量 shortLinkClient,复用连接池

🟢 说明

  1. 缺少单元测试 — 如需要可以补充 shortlink_test.go
  2. comment_feed.go 空白字符 — 是 go fmt 自动格式化的结果,下次会单独 commit

Commit: fix: address PR review comments

@kimi-agent-bot
Copy link
Contributor

🤔 Kimi Code Agent 正在处理你的请求...

@kimi-agent-bot
Copy link
Contributor

@zero469 感谢快速修复!已逐项核对最新 commit fix: address PR review comments,确认所有问题均已正确解决:

# 问题 状态 验证结果
1 正则重复编译 ✅ 已修复 feedIDPatterns 已提升为包级 varextractFeedIDFromPath 仅遍历
2 Handler 签名不一致 ✅ 已修复 使用 ResolveShortLinkArgs struct,与其他 handler 保持一致
3 xsec_token 泄露日志 ✅ 已修复 日志仅保留 feed_id,敏感信息已移除
5 HTTP Client 未复用 ✅ 已修复 shortLinkClient 为包级变量,连接池可复用

代码质量 LGTM 👍,从我这边来看没有其他阻塞问题了。

关于单元测试(#4):建议后续补充 shortlink_test.go,至少覆盖以下场景:

  • normalizeShortLinkURL 对各种输入格式的处理(含/不含协议前缀、非法域名等)
  • parseRedirectURL 对不同路径格式的 feed_id 提取(/discovery/item//explore//note/
  • extractFeedIDFromPath 的边界情况(空路径、不匹配路径)

可以作为 follow-up PR 提交,不阻塞本次合并。

@kimi-agent-bot
Copy link
Contributor

已确认最新 diff 中的修复情况,下面逐项验证:

✅ 正则提升为包级变量feedIDPatterns 已是 var 包级声明,extractFeedIDFromPath 仅做遍历,无重复编译。

✅ Handler 签名统一handleResolveShortLink(ctx context.Context, args ResolveShortLinkArgs) 与其他 handler 一致,ResolveShortLinkArgs 使用 typed struct。

✅ xsec_token 已从日志移除 — 日志仅保留 logrus.Infof("解析成功: feed_id=%s", feedID),敏感信息不再泄露。

✅ HTTP Client 复用shortLinkClient 为包级变量,连接池可复用。

一切就绪,发布确认评论:
已完成对修复 commit 的逐项验证并发布确认评论。所有 4 项修改均正确落地,代码质量 LGTM,无其他阻塞问题。同时建议后续以 follow-up PR 补充单元测试。

@zero469
Copy link
Author

zero469 commented Mar 18, 2026

@xpzouying 请求 review,已解决 Kimi bot 的所有审查意见 🙏

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant