get_video_comments
Retrieve comments from Bilibili videos with pagination, sorting options, and nested replies support in Markdown or JSON format using valid Bilibili credentials.
Instructions
获取 B 站视频的评论内容,支持分页、排序和楼中楼回复。注意:需要有效的 B 站 Cookie 才能正常工作。
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| bvid | No | B 站视频 BV 号(与 aid 二选一,必须提供其中之一) | |
| aid | No | B 站视频 AV 号(与 bvid 二选一,必须提供其中之一) | |
| page | No | 页码,默认为 1 | |
| pageSize | No | 每页数量,范围 1-20,默认 20 | |
| sort | No | 排序方式: 0 按时间,1 按热度 | |
| includeReplies | No | 是否包含楼中楼回复 | |
| outputFormat | No | 输出格式: markdown 或 json | markdown |
| cookie | No | B 站 Cookie(可选)。如果已设置环境变量,则无需提供。 |
Implementation Reference
- src/tools/video.js:13-53 (handler)Main handler function that executes the get_video_comments tool logic. Validates parameters, retrieves video ID, fetches comments from Bilibili API, and formats the output as either markdown or JSON.
export async function getVideoComments(args, api) { try { const { bvid, aid, page = 1, pageSize = 20, sort = 0, includeReplies = true, outputFormat = "markdown" } = args; const cookie = getValidCookie(args.cookie); if (!cookie) { throw new McpError(ErrorCode.InvalidParams, "必须提供有效的 B 站 Cookie。请通过参数传入或设置 BILIBILI_SESSDATA 环境变量。"); } if (!bvid && !aid) throw new McpError(ErrorCode.InvalidParams, "必须提供 bvid 或 aid 之一"); if (pageSize < 1 || pageSize > 20) throw new McpError(ErrorCode.InvalidParams, "pageSize 必须在 1-20 之间"); if (![0, 1].includes(sort)) throw new McpError(ErrorCode.InvalidParams, "sort 必须是 0 或 1"); if (!["markdown", "json"].includes(outputFormat)) throw new McpError(ErrorCode.InvalidParams, "outputFormat 必须是 markdown 或 json"); const videoIdForRef = bvid || `av${aid}`; let oid = aid; if (bvid && !aid) { const videoInfo = await api.getVideoInfo(bvid, cookie); oid = videoInfo.aid; } const response = await api.fetchComments(oid, page, pageSize, sort, cookie, videoIdForRef); if (response.data.code !== 0) { const errorMsg = getApiErrorMessage(response.data.code, response.data.message); throw new McpError(ErrorCode.InternalError, `B 站 API 错误 (${response.data.code}): ${errorMsg}`); } // 构建楼中楼获取函数 const fetchRepliesFn = (comment) => api.fetchReplies(oid, comment.rpid, cookie, videoIdForRef); if (outputFormat === "json") { const jsonResponse = await generateJsonResponse(response.data.data, includeReplies, fetchRepliesFn); return { content: [{ type: "text", text: JSON.stringify(jsonResponse, null, 2) }] }; } else { const markdownResponse = await generateVideoMarkdown(response.data.data, includeReplies, fetchRepliesFn); return { content: [{ type: "text", text: markdownResponse }] }; } } catch (error) { return { content: [{ type: "text", text: `❌ 获取评论失败: ${error.message}` }] }; } } - src/server.js:38-58 (registration)Tool registration defining the get_video_comments tool with its name, description, and inputSchema. The schema specifies parameters like bvid/aid (required), page, pageSize, sort, includeReplies, outputFormat, and cookie.
name: "get_video_comments", description: "获取 B 站视频的评论内容,支持分页、排序和楼中楼回复。注意:需要有效的 B 站 Cookie 才能正常工作。", inputSchema: { type: "object", properties: { bvid: { type: "string", description: "B 站视频 BV 号(与 aid 二选一,必须提供其中之一)" }, aid: { type: "string", description: "B 站视频 AV 号(与 bvid 二选一,必须提供其中之一)" }, page: { type: "number", default: 1, description: "页码,默认为 1" }, pageSize: { type: "number", default: 20, description: "每页数量,范围 1-20,默认 20" }, sort: { type: "number", default: 0, description: "排序方式: 0 按时间,1 按热度" }, includeReplies: { type: "boolean", default: true, description: "是否包含楼中楼回复" }, outputFormat: { type: "string", default: "markdown", description: "输出格式: markdown 或 json" }, cookie: { type: "string", description: "B 站 Cookie(可选)。如果已设置环境变量,则无需提供。" } }, anyOf: [ { required: ["bvid"] }, { required: ["aid"] } ] }, annotations: { title: "B站视频评论获取", readOnlyHint: true, openWorldHint: false } }, - src/server.js:82-83 (registration)Handler invocation in the CallToolRequestSchema switch statement that routes requests to the getVideoComments function when the tool name matches 'get_video_comments'.
case "get_video_comments": return await getVideoComments(args, this.api); - src/utils/cookie.js:24-37 (helper)Helper function that validates and retrieves the Bilibili cookie, prioritizing the passed parameter over the BILIBILI_SESSDATA environment variable.
export function getValidCookie(cookieParam) { // 优先使用传入的 cookie 参数 if (cookieParam && validateCookie(cookieParam)) { return cookieParam; } // 检查 BILIBILI_SESSDATA 环境变量 const sessdata = process.env.BILIBILI_SESSDATA; if (sessdata && typeof sessdata === 'string' && sessdata.trim()) { return buildCookieFromSessdata(sessdata.trim()); } return null; } - src/formatters/markdown.js:90-116 (helper)Helper function that formats video comments as Markdown, including pagination info, comment list with user details, and optional nested replies (楼中楼).
export async function generateVideoMarkdown(pageInfo, includeReplies, fetchRepliesFn) { const { currentPage, totalCount, totalPages } = buildPagination(pageInfo); let md = `## 📺 B 站评论分析结果\n\n`; md += `📄 **当前显示**: 第 ${currentPage} / ${totalPages} 页\n`; md += `📊 **评论总数**: ${totalCount} 条\n\n`; const allComments = aggregateComments(pageInfo); if (allComments.length === 0) { md += "😴 **此页面没有评论。**\n\n"; md += "✅ 分析完成。如果视频有更多评论,请尝试请求其他页面。"; return md; } const allReplies = await fetchAllReplies(allComments, includeReplies, fetchRepliesFn); md += "### 💬 评论列表\n"; allComments.forEach((comment, index) => { md += formatCommentWithReplies(comment, allReplies[index] || []); }); md += "---\n\n"; md += buildPaginationFooter(currentPage, totalPages); return md; }