dsh-video-evidence
Allows searching Bilibili public videos for a query and locating the specific video segments that answer it, with timestamps and direct playback links.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@dsh-video-evidence在B站搜‘Transformer原理’,并告诉我视频中讲到Self-Attention的时间点"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
dsh-video-evidence
兼容的 Harness 版本
已验证支持:DeepSeek Harness
0.1.0-rc.6(通过@deepseek-ai/dsh-mcp-client,跨 profile 可用)。最新版
0.1.2-alpha.3:适配计划进行中,暂无确切完成时间。升级前请先备份你的 DSH 安装。
给 DeepSeek Harness (dsh) 的 MCP 插件:按问题检索 B 站视频,并定位到真正讲到该内容的片段(带时间戳、可直接回跳)。
不是"搜到视频"就结束,而是回答"这个问题的答案在视频的第几分钟"。
用户:Spring Boot 项目怎么创建?
→ 1. https://www.bilibili.com/video/BV17m411d7Ny?t=0
环境准备、JDK 版本选择 …
→ 2. https://www.bilibili.com/video/BV17m411d7Ny?t=60
打开 IDEA,新建 Project,选择 Spring Initializr …
→ 3. https://www.bilibili.com/video/BV17m411d7Ny?t=90
填写 Group / Artifact,勾选依赖,Finish …Related MCP server: video_agent
为什么做这个
市面上的"跨模态检索 / 智能搜索"插件,绝大多数做的是网页和文本这一层:给一段查询,返回一堆链接和摘要。这一层已经很挤了,重复造轮子没有意义。
真正还没有被好好解决的,是视频内部的片段级定位:
搜索引擎只能告诉你"这个视频可能相关",不会告诉你相关的是哪 30 秒;
视频平台自带的字幕搜索依赖 UP 主上传字幕,覆盖率和质量都靠运气;
对教程、课程录像、会议发言这类内容,用户真正要的从来不是"整片",而是"讲到 X 的那一小段"。
本插件补的就是这一段:公开视频 → 本地转写 → 片段级检索 → 可回跳的时间戳证据。全部本地完成,不依赖任何付费 API 或第三方检索服务。
工作流程
┌──────────────────────── dsh (MCP client) ────────────────────────┐
│ mcp__video_evidence__video_search / __video_cache_stats │
└───────────────────────────────┬──────────────────────────────────┘
│ stdio / JSON-RPC
┌───────────────────────────────▼──────────────────────────────────┐
│ server.js (Node 常驻进程,只做协议转发) │
│ · 双分帧解析:NDJSON ⇄ Content-Length │
│ · 收到 tools/call 才 spawn Python worker │
└───────────────────────────────┬──────────────────────────────────┘
│ spawn(按需,用完即退)
┌───────────────────────────────▼──────────────────────────────────┐
│ worker.py → server.py │
│ 1. bili.py B 站 wbi 签名搜索 → 取 cid → 取 360P 流地址 │
│ 2. asr.py ffmpeg 拉流转 16k 单声道 wav │
│ faster-whisper (base/int8, CPU) 转写 │
│ 3. cache.py SQLite 按 BV 号缓存转写结果(TTL 30 天) │
│ 4. evidence.py 中文 BM25(单字 + 二元组)打分 │
│ 45s 滑窗 / 30s 步长切块 → 排序取 top-k │
└──────────────────────────────────────────────────────────────────┘为什么是 Node 常驻 + Python worker: dsh 的 dsh-mcp-client spawn 之后会立刻发 initialize,跟 Python 常驻进程握手不稳定(实测 4 次启动、0 条消息收到)。Node 冷启动足够快,而真正的检索/ASR 留在 Python 侧,只在工具被真正调用时才拉起,空闲时不占资源。
安装
前置依赖
依赖 | 说明 |
Node.js | 18+,只用到内置模块,无需 |
Python | 3.9+ |
ffmpeg | 必须在 PATH 中,或用 |
faster-whisper |
|
git clone https://github.com/wjxn13/dsh-video-evidence.git
cd dsh-video-evidence
# Windows
python -m venv .venv
.venv\Scripts\pip install -r requirements.txt
# macOS / Linux
python3 -m venv .venv
# 注意:server.js 里写死了 .venv/Scripts/python.exe(Windows 布局)。
# 非 Windows 需要把 server.js 中 PY 的路径改成 .venv/bin/python。
.venv/bin/pip install -r requirements.txt首次调用 video_search 时,faster-whisper 会自动下载 base 模型到 models/base/(约 145MB)。也可以手动放置,或用 ASR_MODEL_DIR 指向已有模型目录。
接入 dsh
在用户级 ~/.dsh/cordis.patch.yml 里追加(路径改成你自己的):
- insert:
- id: mcp-video-evidence
name: "@deepseek-ai/dsh-mcp-client"
config:
serverName: video_evidence # ← 决定工具名前缀:mcp__video_evidence__*
transport: stdio
command: "C:/path/to/node.exe"
args:
- "C:/path/to/dsh-video-evidence/server.js"
cwd: "C:/path/to/dsh-video-evidence"
env:
PYTHONIOENCODING: "utf-8"
PYTHONUNBUFFERED: "1"
toolCallTimeoutMs: 240000几个关键点:
name要填@deepseek-ai/dsh-mcp-client,这是 dsh 的 MCP loader 包名,不是随便起的名字。serverName决定工具名前缀:mcp__<serverName>__<toolName>,所以这里是mcp__video_evidence__video_search。command用 node 的绝对路径。dsh 会清理子进程环境,不能依赖 PATH。cwd要指向插件目录,server.js依赖它定位.venv和worker.py。toolCallTimeoutMs给足(这里 240 秒)。首次查询要拉流 + 转写,默认的短超时会让工具在结果出来前被判失败。
然后重启 dsh——配置只在启动时加载。用 dsh --dump-config --profile web 可以确认条目有没有被正确合并进去。
⚠️
id不能和已有条目重复。 cordis loader 不允许同 id 覆盖,重复会直接让 dsh 启动崩溃(报 duplicate loader entry)。改配置前先备份cordis.patch.yml。
工具
video_search
参数 | 类型 | 默认值 | 说明 |
| string | 必填 | 要检索的问题或关键词 |
| integer | 3 | 最多处理几个候选视频(1–5) |
| integer | 5 | 返回几个最相关片段(1–10) |
| integer | 300 | 每个视频最多转写前多少秒(60–1800) |
| integer | 170 | 总时间预算,超时就返回已经拿到的结果 |
video_cache_stats
无参数。返回已缓存转写的视频数量。
性能实测
本机环境:Windows 11 / Ryzen 7 6800H / RTX 3050 Ti(纯 CPU 推理,不用显卡),faster-whisper base + int8:
项目 | 实测 |
转写速度 | 45 秒音频 ≈ 2.3 秒(约 20 倍实时) |
10 分钟视频 | ≈ 30 秒 |
命中缓存后 | 几乎瞬时(只查 SQLite + BM25) |
也就是说,一次交互式提问(转写前 90–180 秒)通常在几秒内返回。
环境变量
变量 | 默认值 | 说明 |
| PATH 里的 | ffmpeg 可执行文件路径。不设则自动从 PATH 查找,找不到才需要显式指定 |
|
| faster-whisper 模型目录 |
| 空 | B 站 SESSDATA Cookie。不填也能用(搜索/取流正常),填了可以尝试走官方字幕通道 |
|
| 转写缓存数据库路径 |
|
| 缓存有效期(秒) |
|
| 默认单视频转写时长上限 |
|
| 默认总时间预算 |
|
| 默认候选视频数 |
|
| 滑窗切块的窗口长度与步长(秒) |
|
| Python worker 超时(毫秒) |
已知限制
只覆盖 B 站公开视频,且只转写前
audio_seconds秒(默认 5 分钟)。长视频靠后的内容需要调大该参数,耗时线性增长。匿名状态下拿不到官方字幕(
subtitles接口返回 0 条),所以一律走本地 ASR,不依赖 UP 主有没有上传字幕。BM25 是词面匹配,不做语义向量检索。换个说法提问可能命中不到,这是有意的取舍——本地 CPU 上跑 embedding 模型会显著拖慢交互式响应。
server.js里 Python 路径写死为.venv/Scripts/python.exe(Windows 布局),非 Windows 需要手动改一处。
排错
工具注册不上去(dsh 里看不到 mcp__video_evidence__*)
这是最坑的一类问题,现象是:配置明明正确(dsh --dump-config --profile web 能看到条目),重启也没用,日志里反复刷 server ready → stdin closed → server ready。
根因:同一个 dsh 实例里存在两套 MCP 分帧格式。
dsh 自带的
dsh-mcp-client发的是 NDJSON(裸 JSON +\n,没有Content-Length头)argo / memorix 这类用标准 MCP SDK 的走的是
Content-Length: N\r\n\r\n{json}
只认 Content-Length 的解析器收 NDJSON 时永远凑不出一条完整消息 → 不回 initialize → dsh 超时关 stdin → 重拉,死循环。
本插件的 server.js 已经用 clientFraming 闩锁 + nextMessage() 做了双格式兼容,响应按客户端格式对称回写。如果你要 fork 改写协议层,这两段不要动。
配套的两个必要细节:
initialize必须回显客户端发来的protocolVersion,不能硬编码。MCP SDK 会校验服务端返回的版本是否在自身支持列表内,硬编码新版会让较老 SDK 判定协商失败并直接关闭 stdin。ping/resources/list/prompts/list要返回空结果而不是-32601。 客户端探测能力时收到 method not found 会判定连接不健康,进而断连重连。
怎么定位: 在 stdin 的 data 回调里打第一条原始数据:
if (!gotAny) {
gotAny = true;
log("FIRST stdin data: raw=" + JSON.stringify(d.toString("utf8").slice(0, 400)));
}看到 {"method":"initialize",...} 后面直接跟 \n、没有 Content-Length,就确认是 NDJSON。
B 站搜索返回空(HTTP 200、code:0、但结果为空)
wbi 签名的坑:urllib.parse.urlencode 默认用 quote_plus,空格编码成 +;B 站服务端按 %20 重算签名,校验不通过就静默拒绝。
# 必须用 quote(空格 -> %20),不能用默认的 quote_plus(空格 -> +)
q = urllib.parse.urlencode(p, quote_via=urllib.parse.quote)bili.py 里已经修好,并且加了非 wbi 的 legacy 接口作为兜底。
目录结构
dsh-video-evidence/
├── server.js # Node 常驻 MCP server(协议层:双分帧 + 方法分发)
├── worker.py # Python worker 入口(CLI,读 JSON 参数、打 JSON 结果)
├── server.py # 业务编排:搜索 → 取流 → 转写 → 检索
├── bili.py # B 站 API 客户端(wbi 签名、搜索、cid、playurl)
├── asr.py # ffmpeg 拉流 + faster-whisper 转写
├── evidence.py # 中文 BM25 打分 + 滑窗切块
├── cache.py # SQLite 转写缓存(按 BV 号,TTL 30 天)
└── requirements.txtLicense
MIT
This server cannot be deployed
Maintenance
Related MCP Connectors
Extract YouTube transcripts, search what was said, and read on-screen frames with cited timestamps.
YouTube video search with transcript extraction as first-class output.
- ShortyOAuthcom.aishorty
Summarize and transcribe videos, audio, documents and web pages; subtitles; search your library.
Video knowledge base for agents: search your library's transcripts, keyframes and on-screen text.
Related MCP Servers
- AlicenseNot gradedqualityCmaintenanceEnables Claude to transcribe Bilibili videos, search by intent, and analyze local video files, returning structured summaries with key points and verdicts.Apache 2.0
- AlicenseNot gradedqualityBmaintenanceEnables probing, ingesting, transcribing, and searching Bilibili videos with timestamped evidence and frame extraction, without reading browser cookies.1 npmMIT
- AlicenseNot gradedqualityCmaintenanceEnables transcribing public Douyin videos to text using a local AI model, providing read-only tools for transcription, search, and listing.MIT
- AlicenseNot gradedqualityBmaintenanceEnables search across videos you've watched via transcripts, on-screen text, and frames, citing exact timestamps. Point it at videos, channels, or playlists; it indexes everything locally and answers queries with deep links to the exact second.2MIT