关于 docsonar
docsonar 是面向本地文档文件夹的 MCP 服务,适合希望在设备上检索文件而不上传内容的用户。它结合关键词与语义搜索,并将访问限制为只读且仅限已注册文件夹。
主要功能
- 01关键词与语义混合搜索
- 02索引 TXT、Markdown、PDF、DOCX 和 HTML
- 03读取匹配的文件段落
- 04查找语义相似的文档
- 05增量索引与状态查询
适用场景
- 01按主题或标识符搜索笔记
- 02定位 PDF 段落并获取页码
- 03文件变更后刷新索引
MCP 相关
向 MCP 客户端提供八种工具,用于注册文件夹、索引、搜索、读取文件和查看索引状态。支持 stdio 与 HTTP 传输。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
工具
本地服务需要在你的电脑上运行后才能读取工具,维护者暂未提供工具列表。
详细介绍
docsonar
与文件夹对话。 本地文档搜索 MCP 服务——你的文件永远不会离开你的设备。
docsonar 会将文档文件夹索引到单个 SQLite 数据库中,让任何 MCP 客户端(Claude Desktop、Claude Code 等)都能对这些文档进行关键词与语义混合搜索。完全离线、零配置,设计上只读。
快速开始
Claude Code
添加到项目的 .mcp.json(或所有项目通用的 ~/.claude.json)中:
{
"mcpServers": {
"docsonar": {
"command": "uvx",
"args": ["docsonar"]
}
}
}
Claude Desktop
添加到 claude_desktop_config.json(Settings → Developer → Edit Config)中:
{
"mcpServers": {
"docsonar": {
"command": "uvx",
"args": ["docsonar"]
}
}
}
如果要从源代码检出目录运行:"command": "uv", "args": ["run", "--directory", "/path/to/docsonar", "docsonar"]。
然后直接与它对话:“添加我的 ~/Documents/notes 文件夹,并查找所有关于季度规划的内容。” 首次建立索引时会下载嵌入模型(约 130 MB,仅需下载一次);下载期间关键词搜索仍可立即使用。
如需使用 HTTP 而非 stdio:uvx docsonar --transport http --port 8365。
工具
| 工具 | 用途 |
|---|---|
add_folder(path, include_globs?, exclude_globs?) | 注册文件夹;索引在后台开始 |
remove_folder(path) | 取消注册并清除其索引数据 |
list_folders() | 已注册文件夹及其文件数量和最近索引时间 |
search(query, top_k?, folder?, file_type?, mode?) | 关键词与语义混合搜索(RRF 融合);按相关性排序的段落,包含文件路径、标题/页码位置和摘要 |
read_file(path, start_line?, end_line?) | 文件文本(pdf/docx/html 提取的文本);拒绝读取已注册文件夹以外的路径 |
find_similar(path, top_k?) | 与指定文件语义最相似的文档 |
reindex(folder?, force?) | 增量刷新:重新索引新增或更改的文件,并清除已删除文件的数据 |
index_status() | 索引总数、嵌入模型状态、后台任务进度和失败文件 |
支持的格式:txt、md、pdf(可识别页码,引用格式为 p. 12)、docx、html。
架构
flowchart LR
Client["MCP client<br/>(Claude Desktop / Code)"] <-->|stdio or HTTP| Server["FastMCP server<br/>8 tools"]
Server --> Sec["path security<br/>(registered folders only)"]
Server --> Search["hybrid search<br/>BM25 + cosine KNN + RRF"]
Server --> Worker["background indexer<br/>(worker thread + queue)"]
Worker --> Parsers["parsers<br/>txt · md · pdf · docx · html"]
Parsers --> Chunker["heading/page-aware chunker<br/>~400 tokens, 60 overlap"]
Chunker --> DB[("SQLite<br/>FTS5 + sqlite-vec")]
Search --> DB
Embed["sentence-transformers<br/>bge-small-en-v1.5 (local)"] --> Worker
Embed --> Search
每个文本块都会保存其位置(例如 Setup > Windows 这样的标题路径,或 PDF 页码范围),因此搜索结果可以引用 report.pdf, p. 12。
设计决策
- 从设计上确保只读。 没有写入、移动或删除工具,以后也不会有。服务端只会读取你明确注册的文件夹中的文件,并提供符号链接逃逸防护和严格的路径解析,因此客户端行为失常时的影响范围为零。
- 使用 SQLite 作为唯一存储。 FTS5 在标准库中提供生产级的 BM25 关键词搜索,sqlite-vec 则将向量存储在同一个文件中。只需一个数据库文件,无需运行服务,备份或删除都很简单。
- 默认使用混合搜索。 BM25 和余弦 KNN 排名通过倒数排名融合(k=60)进行合并。精确标识符和罕见词在关键词搜索中表现更好;改述后的问题在语义搜索中表现更好;RRF 无需校准两者之间的分数。调用方可通过
mode强制使用其中一种。 - 使用本地嵌入。 BAAI/bge-small-en-v1.5(384 维)——与经典的 all-MiniLM-L6-v2 属于同一尺寸级别,但在检索基准测试中表现更好。首次建立索引时下载,可在 CPU 上运行,采用延迟加载,因此服务端可瞬间启动。如果模型无法加载,所有功能都会平稳降级为关键词搜索,工具响应也会说明这一点。
- 专为 LLM 调用方设计的工具接口。
search会为每个命中项返回足够的信息(路径、位置、分数、片段),让调用方无需再次往返即可决定接下来读取什么;结果带有稳定的chunk_id;每次降级都会通过note/embedding_note字段在响应中说明,而不是直接失败。这里有意不提供“回答问题”的工具——推理由调用模型完成;docsonar 负责检索。 - 根据内容增量更新,而不只看 mtime。 重新建立索引时会先检查 mtime 和大小,再回退到 SHA-256 内容哈希——内容未变但修改时间有变化的文件会被跳过,已删除的文件会从 FTS 和向量索引中清除。
- 扫描版 PDF 会明确报错。 没有可提取文本的 PDF 会以清晰的原因报告为失败,而不会被静默地作为空文件编入索引。OCR 不在范围内。
基准测试
合成语料库包含 200 个 Markdown 文件(600 个片段);Intel Core Ultra 5 125U(笔记本电脑,仅使用 CPU)、Windows 11、Python 3.12。使用 uv run python scripts/benchmark.py 重现。
| 操作 | 结果 |
|---|---|
| 建立索引,仅关键词搜索 | 0.7 s(≈280 个文件/s) |
| 建立索引,包含嵌入 | 59 s(≈3.4 个文件/s——受嵌入速度限制) |
| 增量重新建立索引,没有更改 | 0.03 s |
| 关键词搜索 | 中位数 8.9 ms |
| 语义搜索 | 中位数 35 ms |
| 混合搜索 | 中位数 54 ms |
| 嵌入模型加载(每个进程一次) | ~21 s |
| 数据库大小 | 2.6 MB(仅关键词搜索时为 1.1 MB) |
配置
无需任何配置。如需自定义,请在平台配置目录中创建 config.toml(Windows:%LOCALAPPDATA%\docsonar\,macOS:~/Library/Application Support/docsonar/,Linux:~/.config/docsonar/):
embedding_model = "BAAI/bge-small-en-v1.5" # any sentence-transformers model
chunk_target_tokens = 400
chunk_max_tokens = 512
chunk_min_tokens = 100
chunk_overlap_tokens = 60
max_file_size_mb = 50
extra_ignore_dirs = ["Archive"]
CLI 标志:--transport stdio|http、--host、--port、--db-path、--config。
索引数据库位于平台数据目录中(Windows:%LOCALAPPDATA%\docsonar\,macOS:~/Library/Application Support/docsonar/,Linux:~/.local/share/docsonar/)。
从源代码运行
需要 uv(它会自动安装正确的 Python 版本):
# Windows
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"
# macOS / Linux
curl -LsSf https://astral.sh/uv/install.sh | sh
git clone https://github.com/ribhav-jain/docsonar.git
cd docsonar
uv sync --dev # creates .venv and installs everything, incl. dev tools
运行测试
uv run pytest # full suite (~2 s, no model download needed)
uv run pytest -v # verbose, one line per test
uv run pytest tests/test_search.py # one file
uv run pytest -k incremental # tests matching a keyword
uv run ruff check . # lint
uv run mypy src tests # strict typecheck
uv run python scripts/benchmark.py # perf numbers (downloads the real model)
运行服务端
uv run docsonar # stdio — for MCP clients (Claude Desktop/Code)
uv run docsonar --transport http --port 8365 # HTTP — for manual testing (Postman, curl)
VS Code:按 F5——HTTP 服务端和测试套件的启动配置位于 .vscode/launch.json。
无需 MCP 客户端即可试用这些工具
HTTP 服务运行后,端点为 http://127.0.0.1:8365/mcp。较新版本的 Postman 可以直接连接(New → MCP Request,传输方式为 HTTP),并以表单形式显示全部 8 个工具。对于原始 HTTP,MCP 使用 POST 传输 JSON-RPC,需要先发送两个初始化调用,然后再调用工具。每个请求都要包含标头 Content-Type: application/json 和 Accept: application/json, text/event-stream:
// 1. initialize — copy the `mcp-session-id` RESPONSE header and send it
// back as a request header on every call below
{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"postman","version":"1.0"}}}
// 2. initialized notification (expect 202, empty body)
{"jsonrpc":"2.0","method":"notifications/initialized"}
// 3. list the tools
{"jsonrpc":"2.0","id":2,"method":"tools/list"}
然后调用工具——仓库在 examples/sample-docs 中附带了示例语料,供你试用(在 JSON 中使用正斜杠以避免转义;在 Windows 上也可用):
// Register the sample folder (first ever call downloads the embedding model, ~30 s)
{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"add_folder",
"arguments":{"path":"C:/path/to/docsonar/examples/sample-docs"}}}
// Watch indexing progress until indexing.active is false
{"jsonrpc":"2.0","id":4,"method":"tools/call","params":{"name":"index_status","arguments":{}}}
// Hybrid search — finds the expense-policy PDF, cites its page
{"jsonrpc":"2.0","id":5,"method":"tools/call","params":{"name":"search",
"arguments":{"query":"meal reimbursement limit","top_k":3,"mode":"hybrid"}}}
// Semantic search — no keyword overlap needed
{"jsonrpc":"2.0","id":6,"method":"tools/call","params":{"name":"search",
"arguments":{"query":"how hot should water be for green tea","mode":"semantic"}}}
// Read a hit (works on PDFs too — returns extracted text)
{"jsonrpc":"2.0","id":7,"method":"tools/call","params":{"name":"read_file",
"arguments":{"path":"C:/path/to/docsonar/examples/sample-docs/expense-policy.pdf"}}}
// "More like this"
{"jsonrpc":"2.0","id":8,"method":"tools/call","params":{"name":"find_similar",
"arguments":{"path":"C:/path/to/docsonar/examples/sample-docs/espresso-guide.md","top_k":3}}}
// Refresh after files change on disk (force:true rebuilds everything)
{"jsonrpc":"2.0","id":9,"method":"tools/call","params":{"name":"reindex","arguments":{}}}
// Clean up — unregisters the folder and purges its index data
{"jsonrpc":"2.0","id":10,"method":"tools/call","params":{"name":"remove_folder",
"arguments":{"path":"C:/path/to/docsonar/examples/sample-docs"}}}
值得一试的安全检查:使用 read_file 读取注册文件夹之外的任意路径(例如 C:/Windows/System32/drivers/etc/hosts),将返回拒绝信息,而非文件内容。
CI 会在 Linux、macOS 和 Windows 上运行 lint、类型检查和测试套件。
许可证
MIT
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。