bilibili WebMCP
Browser tool configuration for bilibili
Tools (8)
open_article_editor()
Navigate to Bilibili article writing page
Parameters
No parameters
JavaScript Handler
() => {
window.location.href = 'https://member.bilibili.com/article-text/home';
return { success: true, message: 'Opening Bilibili article editor...' };
}
insert_article()
Insert content into Bilibili article editor (must be on member.bilibili.com/article-text/home)
Parameters
JavaScript Handler
(params) => {
// Set title
if (params.title) {
const titleInput = document.querySelector('.article-title input') || document.querySelector('input[placeholder*="标题"]');
if (titleInput) {
titleInput.value = params.title;
titleInput.dispatchEvent(new Event('input', { bubbles: true }));
}
}
// Find editor
const editor = document.querySelector('.ql-editor') || document.querySelector('[contenteditable="true"]');
if (!editor) {
return { success: false, message: 'Editor not found. Open member.bilibili.com/article-text/home first' };
}
editor.innerHTML = params.content;
editor.dispatchEvent(new Event('input', { bubbles: true }));
return { success: true, message: 'Content inserted into Bilibili editor' };
}
bilibili_search()
Search Bilibili videos by keyword
Parameters
JavaScript Handler
(params) => {
const run = async function(args) {
if (!args.keyword) return {error: 'Missing argument: keyword'};
const page = parseInt(args.page) || 1;
const ps = Math.min(parseInt(args.count) || 20, 50);
const order = args.order || 'totalrank';
const params = new URLSearchParams({search_type: 'video', keyword: args.keyword, page: String(page), page_size: String(ps), order});
const resp = await fetch('https://api.bilibili.com/x/web-interface/wbi/search/type?' + params, {credentials: 'include'});
if (!resp.ok) return {error: 'HTTP ' + resp.status, hint: 'Not logged in?'};
const d = await resp.json();
if (d.code !== 0) return {error: d.message || 'API error ' + d.code, hint: 'Not logged in?'};
const stripHtml = s => (s || '').replace(/<[^>]*>/g, '');
const videos = (d.data?.result || []).map(r => ({
bvid: r.bvid,
title: stripHtml(r.title),
author: r.author,
author_mid: r.mid,
description: stripHtml(r.description).substring(0, 200),
duration: r.duration,
play: r.play,
danmaku: r.danmaku,
like: r.like,
favorites: r.favorites,
cover: r.pic?.startsWith('//') ? 'https:' + r.pic : r.pic,
pub_date: r.pubdate ? new Date(r.pubdate * 1000).toISOString() : null,
tags: r.tag || '',
url: 'https://www.bilibili.com/video/' + r.bvid
}));
return {keyword: args.keyword, page, total: d.data?.numResults || 0, count: videos.length, videos};
};
return run(params || {});
}
bilibili_video()
Get Bilibili video details by bvid
Parameters
JavaScript Handler
(params) => {
const run = async function(args) {
const bvid = args.bvid || args._positional?.[0];
if (!bvid) return {error: 'Missing argument: bvid'};
const resp = await fetch('https://api.bilibili.com/x/web-interface/view?bvid=' + encodeURIComponent(bvid), {credentials: 'include'});
if (!resp.ok) return {error: 'HTTP ' + resp.status, hint: 'Not logged in?'};
const d = await resp.json();
if (d.code !== 0) return {error: d.message || 'API error ' + d.code, hint: d.code === -404 ? 'Video not found' : 'Not logged in?'};
const v = d.data;
const result = {
bvid: v.bvid,
aid: v.aid,
title: v.title,
description: v.desc,
cover: v.pic,
duration: v.duration,
duration_text: Math.floor(v.duration / 60) + ':' + String(v.duration % 60).padStart(2, '0'),
author: v.owner?.name,
author_mid: v.owner?.mid,
author_face: v.owner?.face,
category: v.tname,
tags: v.tag || null,
pub_date: v.pubdate ? new Date(v.pubdate * 1000).toISOString() : null,
stat: {
view: v.stat?.view,
like: v.stat?.like,
dislike: v.stat?.dislike,
coin: v.stat?.coin,
favorite: v.stat?.favorite,
share: v.stat?.share,
reply: v.stat?.reply,
danmaku: v.stat?.danmaku
},
pages: (v.pages || []).map(p => ({
page: p.page,
cid: p.cid,
title: p.part,
duration: p.duration
})),
url: 'https://www.bilibili.com/video/' + v.bvid
};
// Also fetch related videos
try {
const resp2 = await fetch('https://api.bilibili.com/x/web-interface/archive/related?bvid=' + encodeURIComponent(bvid), {credentials: 'include'});
const d2 = await resp2.json();
if (d2.code === 0 && d2.data) {
result.related = d2.data.slice(0, 5).map(r => ({
bvid: r.bvid,
title: r.title,
author: r.owner?.name,
view: r.stat?.view,
duration: r.duration,
url: 'https://www.bilibili.com/video/' + r.bvid
}));
}
} catch(e) {}
return result;
};
return run(params || {});
}
bilibili_comments()
Get comments for a Bilibili video
Parameters
JavaScript Handler
(params) => {
const run = async function(args) {
const bvid = args.bvid || args._positional?.[0];
if (!bvid) return {error: 'Missing argument: bvid'};
const pn = parseInt(args.page) || 1;
const ps = Math.min(parseInt(args.count) || 20, 30);
const sort = args.sort !== undefined ? parseInt(args.sort) : 2;
// First get aid from bvid
const viewResp = await fetch('https://api.bilibili.com/x/web-interface/view?bvid=' + encodeURIComponent(bvid), {credentials: 'include'});
if (!viewResp.ok) return {error: 'HTTP ' + viewResp.status, hint: 'Not logged in?'};
const viewData = await viewResp.json();
if (viewData.code !== 0) return {error: viewData.message || 'Failed to get video info', hint: viewData.code === -404 ? 'Video not found' : 'Not logged in?'};
const aid = viewData.data?.aid;
// Fetch comments using aid
const resp = await fetch('https://api.bilibili.com/x/v2/reply?type=1&oid=' + aid + '&pn=' + pn + '&ps=' + ps + '&sort=' + sort, {credentials: 'include'});
if (!resp.ok) return {error: 'HTTP ' + resp.status, hint: 'Not logged in?'};
const d = await resp.json();
if (d.code !== 0) return {error: d.message || 'API error ' + d.code, hint: 'Not logged in?'};
const formatReply = r => ({
rpid: r.rpid_str,
user: r.member?.uname,
user_mid: r.mid,
user_level: r.member?.level_info?.current_level,
content: r.content?.message,
like: r.like,
reply_count: r.rcount,
time: r.ctime ? new Date(r.ctime * 1000).toISOString() : null,
sub_replies: (r.replies || []).slice(0, 3).map(sr => ({
user: sr.member?.uname,
content: sr.content?.message,
like: sr.like,
time: sr.ctime ? new Date(sr.ctime * 1000).toISOString() : null
}))
});
const comments = (d.data?.replies || []).map(formatReply);
// Include top/pinned comments on first page
let top = null;
if (pn === 1 && d.data?.top_replies?.length) {
top = d.data.top_replies.map(formatReply);
}
return {
bvid,
aid,
title: viewData.data?.title,
page: pn,
total: d.data?.page?.count || 0,
count: comments.length,
sort: sort === 0 ? 'by_time' : 'by_likes',
top_comments: top,
comments
};
};
return run(params || {});
}
bilibili_feed()
Get Bilibili dynamic feed (timeline from followed users)
Parameters
JavaScript Handler
(params) => {
const run = async function(args) {
const typeMap = {all: 'all', video: 'video', article: 'article', draw: 'draw'};
const type = typeMap[args.type] || 'all';
const maxCount = parseInt(args.count) || 20;
const resp = await fetch('https://api.bilibili.com/x/polymer/web-dynamic/v1/feed/all?type=' + type + '&page=1', {credentials: 'include'});
if (!resp.ok) return {error: 'HTTP ' + resp.status, hint: 'Not logged in?'};
const d = await resp.json();
if (d.code !== 0) return {error: d.message || 'API error ' + d.code, hint: 'Not logged in?'};
if (!d.data?.items?.length) return {error: 'No feed items', hint: 'Not logged in or not following anyone?'};
const items = (d.data.items || []).slice(0, maxCount).map(item => {
const author = item.modules?.module_author;
const dynamic = item.modules?.module_dynamic;
const stat = item.modules?.module_stat;
const base = {
id: item.id_str,
type: item.type,
url: 'https://www.bilibili.com/opus/' + item.id_str,
author: author?.name,
author_mid: author?.mid,
author_face: author?.face,
pub_time: author?.pub_ts ? new Date(author.pub_ts * 1000).toISOString() : null,
pub_action: author?.pub_action,
text: dynamic?.desc?.text || null,
comment_count: stat?.comment?.count,
forward_count: stat?.forward?.count,
like_count: stat?.like?.count
};
// Video type
if (item.type === 'DYNAMIC_TYPE_AV' && dynamic?.major?.archive) {
const arc = dynamic.major.archive;
base.video = {
bvid: arc.bvid,
title: arc.title,
cover: arc.cover,
duration_text: arc.duration_text,
play: arc.stat?.play,
danmaku: arc.stat?.danmaku,
url: 'https://www.bilibili.com/video/' + arc.bvid
};
}
// Draw/image type
if (item.type === 'DYNAMIC_TYPE_DRAW' && dynamic?.major?.draw) {
base.images = (dynamic.major.draw.items || []).map(img => img.src);
}
// Article type
if (item.type === 'DYNAMIC_TYPE_ARTICLE' && dynamic?.major?.article) {
const art = dynamic.major.article;
base.article = {
id: art.id,
title: art.title,
covers: art.covers,
url: 'https://www.bilibili.com/read/cv' + art.id
};
}
return base;
});
return {type, count: items.length, has_more: !!d.data.has_more, items};
};
return run(params || {});
}
bilibili_history()
Get Bilibili watch history
Parameters
JavaScript Handler
(params) => {
const run = async function(args) {
const ps = Math.min(parseInt(args.count) || 20, 50);
const resp = await fetch('https://api.bilibili.com/x/web-interface/history/cursor?ps=' + ps + '&type=archive', {credentials: 'include'});
if (!resp.ok) return {error: 'HTTP ' + resp.status, hint: 'Not logged in?'};
const d = await resp.json();
if (d.code !== 0) return {error: d.message || 'API error ' + d.code, hint: 'Not logged in?'};
if (!d.data?.list) return {error: 'No history data', hint: 'Not logged in?'};
const items = (d.data.list || []).map(h => {
const progress = h.progress === -1 ? 'completed' : h.progress > 0 ? Math.floor(h.progress / 60) + ':' + String(h.progress % 60).padStart(2, '0') : 'not_started';
const duration_text = h.duration > 0 ? Math.floor(h.duration / 60) + ':' + String(h.duration % 60).padStart(2, '0') : null;
return {
bvid: h.history?.bvid,
title: h.title,
author: h.author_name,
author_mid: h.author_mid,
cover: h.cover,
duration: h.duration,
duration_text,
progress,
progress_seconds: h.progress,
view_at: h.view_at ? new Date(h.view_at * 1000).toISOString() : null,
tag_name: h.tag_name,
url: h.history?.bvid ? 'https://www.bilibili.com/video/' + h.history.bvid : null
};
});
return {count: items.length, items};
};
return run(params || {});
}
bilibili_me()
Get current Bilibili logged-in user info
Parameters
No parameters
JavaScript Handler
(params) => {
const run = async function(args) {
const resp = await fetch('https://api.bilibili.com/x/web-interface/nav', {credentials: 'include'});
if (!resp.ok) return {error: 'HTTP ' + resp.status, hint: 'Not logged in?'};
const d = await resp.json();
if (d.code !== 0) return {error: d.message || 'API error ' + d.code, hint: 'Not logged in?'};
if (!d.data?.isLogin) return {error: 'Not logged in', hint: 'Please log in to bilibili.com first'};
const u = d.data;
const result = {
mid: u.mid,
username: u.uname,
url: 'https://space.bilibili.com/' + u.mid,
face: u.face,
level: u.level_info?.current_level,
coins: u.money,
vip: u.vipType > 0,
vip_type: u.vipType === 1 ? 'monthly' : u.vipType === 2 ? 'annual' : 'none',
vip_label: u.vip_label?.text || null,
moral: u.moral,
email_verified: u.email_verified === 1,
tel_verified: u.mobile_verified === 1,
follower: null,
following: null
};
try {
const statResp = await fetch('https://api.bilibili.com/x/web-interface/nav/stat', {credentials: 'include'});
const statData = await statResp.json();
if (statData.code === 0 && statData.data) {
result.follower = statData.data.follower;
result.following = statData.data.following;
}
} catch(e) {}
return result;
};
return run(params || {});
}
🔌 Chrome MCP Server Extension
Use these tools with Claude, ChatGPT, and other AI assistants.
How to Use WebMCP
WebMCP tools are designed for browser extensions or automation frameworks. The browser extension matches the current URL against the pattern and executes the JavaScript handler when the tool is invoked.
API Endpoint:
GET /api/webmcp/match?url=https://www.bilibili.com/...