import assert from "node:assert/strict"; import test from "node:test"; import { collectMetricsFromMcp, collectXhsMetricsFromMcp, resolveAccountProfileFromMcp, resolveCollectionMcpConfig, resolveXhsPublicAccountDetails, resolveXhsPublicAccountId, resolveXhsAccountProfileFromMcp, resolveXhsProfileDetailsFromMcp, xhsNoteIdFromUrl, } from "../lib/mcp-collection-client.ts"; function sse(payload, options = {}) { return new Response( `event: message\ndata: ${JSON.stringify(payload)}\n\n`, { status: 200, ...options, headers: { "content-type": "text/event-stream", ...(options.headers ?? {}), }, }, ); } function toolEnvelope(payload, isError = false) { return { jsonrpc: "2.0", id: "tool-call", result: { isError, content: [ { type: "text", text: JSON.stringify(payload), }, ], }, }; } function createFakeMcp(toolResults, redirects = {}) { let toolIndex = 0; const calls = []; const fetchImpl = async (url, init) => { if (!init?.body) { const requestUrl = String(url); calls.push({ url: requestUrl, body: null, headers: new Headers(init?.headers) }); const location = redirects[requestUrl]; if (!location) throw new Error(`Unexpected public request: ${requestUrl}`); return new Response("", { status: 302, headers: { location } }); } const body = JSON.parse(init.body); calls.push({ url: String(url), body, headers: new Headers(init.headers) }); if (body.method === "initialize") { return sse( { jsonrpc: "2.0", id: body.id, result: { protocolVersion: "2025-03-26", capabilities: { tools: { listChanged: false } }, serverInfo: { name: "ai-tool-center", version: "test" }, }, }, { headers: { "mcp-session-id": "session-test" } }, ); } if (body.method === "notifications/initialized") { return new Response("", { status: 202 }); } if (body.method === "tools/call") { const result = toolResults[toolIndex]; toolIndex += 1; return sse(result); } throw new Error(`Unexpected MCP method: ${body.method}`); }; return { calls, fetchImpl }; } test("collects likes, comments and favorites from the verified MCP shape", async () => { const { calls, fetchImpl } = createFakeMcp([ toolEnvelope({ http_status: 200, response: { code: 200, success: true, msg: "获取内容详情成功", data: { likes: "483", comments: "41", collects: "519", }, }, }), ]); const result = await collectXhsMetricsFromMcp( "https://www.xiaohongshu.com/discovery/item/test?xsec_token=valid", { endpoint: "https://collector.example/mcp", key: "test-key", }, fetchImpl, ); assert.deepEqual(result, { likes: 483, comments: 41, collects: 519, shares: 0, }); assert.equal(calls.length, 3); assert.equal(calls[2].body.params.name, "fetch_content_detail"); assert.equal(calls[2].body.params.arguments.request.include_comments, false); assert.equal(calls[2].headers.get("mcp-session-id"), "session-test"); assert.equal(new URL(calls[0].url).searchParams.get("key"), "test-key"); }); test("collects through a stateless MCP server without a session header", async () => { const calls = []; const fetchImpl = async (_url, init) => { const body = JSON.parse(init.body); calls.push({ body, headers: new Headers(init.headers) }); if (body.method === "initialize") { return sse({ jsonrpc: "2.0", id: body.id, result: { protocolVersion: "2025-03-26", capabilities: { tools: { listChanged: false } }, serverInfo: { name: "ai-tool-center", version: "production" }, }, }); } if (body.method === "tools/call") { return sse( toolEnvelope({ response: { code: 200, success: true, data: { likes: 12, comments: 3, collects: 6 }, }, }), ); } throw new Error(`Unexpected MCP method: ${body.method}`); }; const result = await collectXhsMetricsFromMcp( "https://www.xiaohongshu.com/explore/test", { endpoint: "https://collector.example/mcp", key: "test-key" }, fetchImpl, ); assert.deepEqual(result, { likes: 12, comments: 3, collects: 6, shares: 0 }); assert.deepEqual( calls.map((call) => call.body.method), ["initialize", "tools/call"], ); assert.equal(calls[1].headers.get("mcp-session-id"), null); }); test("resolves the real XHS account profile from a submitted note link", async () => { const { calls, fetchImpl } = createFakeMcp([ toolEnvelope({ response: { code: 200, success: true, data: { note_detail: { products: [ { content_tags: [ { notes: [ { ip_location: "重庆", note_id: "6a671108000000000f004bef", user: { nickname: "N我的麻辣烫好了吗", profile_url: "https://www.xiaohongshu.com/user/profile/6905cbca0000000037009f49", red_id: "94329495984", user_id: "6905cbca0000000037009f49", }, }, ], }, ], }, ], }, }, }, }), toolEnvelope({ response: { code: 200, success: true, data: { fans_count: "734", }, }, }), ]); const profile = await resolveXhsAccountProfileFromMcp( "https://www.xiaohongshu.com/discovery/item/6a671108000000000f004bef", "回填昵称", { endpoint: "https://collector.example/mcp", key: "test-key", }, fetchImpl, ); assert.deepEqual(profile, { platformUid: "6905cbca0000000037009f49", nickname: "N我的麻辣烫好了吗", profileUrl: "https://www.xiaohongshu.com/user/profile/6905cbca0000000037009f49", redId: "94329495984", ipLocation: "重庆", followers: 734, gender: "", bio: "", recentNoteTitles: [], providerTags: [], }); const mcpCalls = calls.filter((call) => call.body?.method); assert.equal(mcpCalls.length, 4); assert.equal( mcpCalls[2].body.params.name, "fetch_content_detail", ); assert.equal( mcpCalls[2].body.params.arguments.request.link, "https://www.xiaohongshu.com/discovery/item/6a671108000000000f004bef", ); assert.equal(mcpCalls[3].body.params.name, "parse_xhs_user_summary"); assert.equal( mcpCalls[3].body.params.arguments.request.url, "https://www.xiaohongshu.com/user/profile/6905cbca0000000037009f49", ); assert.equal( xhsNoteIdFromUrl( "https://www.xiaohongshu.com/explore/6a671108000000000f004bef", ), "6a671108000000000f004bef", ); }); test("resolves followers directly from the supported XHS user summary tool", async () => { const { calls, fetchImpl } = createFakeMcp([ toolEnvelope({ response: { code: 200, success: true, msg: "用户摘要解析成功", data: { user: { fansCount: 6, ipLocation: "福建", nickname: "555 五", userId: "1020668113", gender: "女", desc: "分享城市周末与美食", tags: ["本地生活"], }, notes: [ { note_id: "note-1", title: "长沙湘菜探店", url: "https://www.xiaohongshu.com/explore/note-1", }, ], }, }, }), ]); const details = await resolveXhsProfileDetailsFromMcp( "https://www.xiaohongshu.com/user/profile/5fb21d32000000000101c23e", { endpoint: "https://collector.example/mcp", key: "test-key", }, fetchImpl, ); assert.deepEqual(details, { nickname: "555 五", followers: 6, redId: "1020668113", ipLocation: "福建", gender: "女", bio: "分享城市周末与美食", recentNoteTitles: ["长沙湘菜探店"], providerTags: ["本地生活"], }); assert.equal(calls[2].body.params.name, "parse_xhs_user_summary"); }); test("resolves an xhslink short URL before requesting the author profile", async () => { const fakeMcp = createFakeMcp([ toolEnvelope({ response: { code: 200, success: true, data: { note_detail: { note_id: "6a572da40000000021018bd2", ip_location: "上海", user: { nickname: "短链作者", user_id: "6905cbca0000000037009f49", }, }, }, }, }), toolEnvelope({ response: { code: 200, success: true, data: { red_id: "1020668113", followers: "1.2万", }, }, }), ]); const fetchImpl = async (url, init) => { if (String(url) === "http://xhslink.cn/o/AJFyP5dnj7O") { return new Response(null, { status: 302, headers: { location: "https://www.xiaohongshu.com/discovery/item/6a572da40000000021018bd2?xsec_token=valid", }, }); } return fakeMcp.fetchImpl(url, init); }; const profile = await resolveXhsAccountProfileFromMcp( "http://xhslink.cn/o/AJFyP5dnj7O", "回填昵称", { endpoint: "https://collector.example/mcp", key: "test-key", }, fetchImpl, ); assert.equal(profile.nickname, "短链作者"); assert.equal(profile.platformUid, "6905cbca0000000037009f49"); assert.equal(profile.redId, "1020668113"); assert.equal(profile.followers, 12_000); assert.equal( profile.profileUrl, "https://www.xiaohongshu.com/user/profile/6905cbca0000000037009f49", ); assert.equal( fakeMcp.calls[2].body.params.arguments.request.link, "http://xhslink.cn/o/AJFyP5dnj7O", ); }); test("uses the public note author when MCP profile lookup fails", async () => { const fakeMcp = createFakeMcp([ toolEnvelope( { response: { code: 500, success: false, msg: "作者详情暂时不可用", }, }, true, ), ]); const fetchImpl = async (url, init) => { if (String(url) === "http://xhslink.cn/o/AJFyP5dnj7O") { return new Response( '', { status: 200, headers: { "content-type": "text/html; charset=utf-8", location: "https://www.xiaohongshu.com/discovery/item/6a572da40000000021018bd2?xsec_token=valid", }, }, ); } if ( String(url) === "https://www.xiaohongshu.com/user/profile/5fb21d32000000000101c23e" ) { return new Response( '', { status: 200 }, ); } return fakeMcp.fetchImpl(url, init); }; const profile = await resolveXhsAccountProfileFromMcp( "http://xhslink.cn/o/AJFyP5dnj7O", "回填昵称", { endpoint: "https://collector.example/mcp", key: "test-key", }, fetchImpl, ); assert.deepEqual(profile, { platformUid: "5fb21d32000000000101c23e", nickname: "555 五", profileUrl: "https://www.xiaohongshu.com/user/profile/5fb21d32000000000101c23e", redId: "1020668113", ipLocation: "待识别", followers: 6, gender: "", bio: "", recentNoteTitles: [], providerTags: [], }); assert.equal( fakeMcp.calls[2].body.params.name, "fetch_content_detail", ); }); test("reads the user-visible Xiaohongshu number from a public profile", async () => { const fetchImpl = async () => new Response( '