关于 Shelfmark
Shelfmark 是本地文档目录,供顾问、研究人员和 Agent 通过元数据发现相关文件,不复制或索引文档正文。它以可配置的目录边界、权利规则和索引状态报告,将受治理的发现与文件访问分开。
主要功能
- 01使用 SQLite 保存文件引用与元数据
- 02支持元数据筛选、搜索和浏览
- 03分别设置权利与保密规则
- 04仅遍历配置目录,不跟随符号链接
- 05报告索引新鲜度与语料状态
适用场景
- 01为新项目查找以往报告和提案
- 02按作者、日期或类型浏览研究资料
- 03让 Agent 先定位文件,再单独访问内容
MCP 相关
向 MCP 客户端提供语料统计、目录浏览、元数据搜索和文件记录查询工具。对于已导入的 .pst/.msg 文件,还可搜索邮件存档。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
工具
本地服务需要在你的电脑上运行后才能读取工具,维护者暂未提供工具列表。
详细介绍
shelfmark
为 AI Agent 提供恰当的上下文,而不是整个文件系统。
Shelfmark 将多年散落的文档转化为供 AI Agent 使用、受治理的上下文地图。Agent 可以发现有哪些内容,了解它们属于哪类资料,并在任务开始消耗上下文来打开文件之前,选出相关文档。
无需迁移文档。无需重复存储内容。无需完美的文件夹结构。
uv tool install shelfmark # or: pipx install shelfmark
本地优先 · 仅元数据 · 受治理的发现 · 专为 MCP 打造
你最宝贵的知识很可能就放在文件夹里
报告、演示文稿、模型、研究资料、提案和工作文档年复一年地积累。有些经过精心整理,有些则堆在拥挤的项目文件夹、旧档案、下载目录,或那些当时才说得通的资料集合里。
这些资料的价值依然存在。问题在于,Agent 无法使用自己无法发现的内容——而给予 Agent 不受限制的文件系统访问权限,并不能解决问题。那只会把查找、解读和筛选数千个文件的工作转移到上下文窗口中。
Shelfmark 会先为 Agent 提供一张地图。
上下文是稀缺资源
Agent 不需要每份文档。它需要知道有哪些内容、哪些可能有用、内容来自哪里,以及是否应该使用。
Shelfmark 是 Agent 与文件之间的发现层。Agent 搜索目录,缩小范围,并只请求任务真正需要的资料——这样,现有文档库就能成为工作上下文,而不会让文件系统变成一个巨大的提示词。
指针目录,而非又一个文档存储库
Shelfmark 会在本地建立一个 SQLite 目录,其中包含引用和衍生元数据:路径、文件名、格式、大小、文档类型、作者、日期、分类、选定的 Office 属性、演示文稿标题,以及可选的内容哈希值。
它不会将文档复制到目录中,也不会为文档正文建立索引,更不会创建另一个需要治理、同步和维护的存储库。文件仍留在原处;目录指向这些文件,并描述从其元数据中能够确定的信息。
找到文档,并不意味着能够看到文档内容。
将杂乱的文件夹变成可供 Agent 使用的上下文
Shelfmark 无需预先设计信息架构。只需将其指向积累多年的项目文件、被遗忘的档案,或那些多年来随手丢入文档的文件夹。
它会利用已有线索建立清单——文件名、扩展名、路径、作者信息、日期、Office 属性,以及你自己的分类规则——从而在这些资料之间创建虚拟结构。Agent 随后可以按文档类型、客户、项目、用途、作者、年份或位置进行搜索,无需移动或重命名任何内容。
即使文件夹永远无法变得整齐,也能轻松浏览其中的资料。
Shelfmark 也会如实说明元数据的局限。名为 final7.pdf 且没有有用属性的文件,如果没人阅读,就无法揭示其含义。Shelfmark 会报告已知信息,而不是编造确定性——正因如此,它会舍弃占位幻灯片标题,而不是列出一套演示文稿实际并不包含的 12 个标题。
专为选择性上下文而构建
- 先发现,再打开。 先搜索数千份参考资料,再决定哪些文件值得关注。
- 保留上下文窗口。 提供精简的目录搜索结果,而非可能无关的整份文档。
- 在既有工作基础上继续推进。 报告、模型、提案、研究和方法,在新任务和未来合作中依然可被发现。
- 让来源信息清晰可见。 路径、日期、作者、文档类型和上下文有助于 Agent 判断相关性。
- 减少盲目探索。 使用结构化搜索,而非反复遍历目录并逐个检查文件。
- 将发现与访问分开。 Shelfmark 帮助识别资料;是否打开原件仍是独立且可控的决定。
根目录就是信任边界
Shelfmark 只遍历你配置的根目录。
不会跟随符号链接。 根目录中的链接会被视为普通文件;否则就会直接跳出你配置的目录树,而且 hash 会打开文件。跳过的链接会被报告,绝不会被悄悄忽略。若要索引另一棵目录树,请将其添加为额外根目录:通过在配置中明确声明来扩大边界,而不是放置一个链接。
任何根目录内都不会放置目录数据库,因为索引自身的数据库会在每次刷新时不断增长。两项检查都会比较解析后的路径,因此 .. 和符号链接根目录都无法绕过检查。
检索层应负责治理
并非所有有用的文档都应一视同仁。Shelfmark 将通常容易混淆的两个问题分开处理:
- 谁拥有或可以复用它? →
rights:OWN/REFERENCE/RESTRICTED - 这份文档能否离开当前上下文? →
confidential:0/1
某种方法可能属于你,但包含该方法的客户交付物仍可能是机密内容。将两者分别建模,让 Agent 能够发现可复用的知识,而不会把所有可发现的内容都视为可自由分享。
匹配你的私密/秘密模式的文件会被标记为 RESTRICTED:任何工具都不会返回其路径、名称、元数据或内容,任何参数都无法覆盖此限制;这些文件绝不会被打开以计算哈希值,数据库也会以只读方式打开。corpus_stats() 会报告整个语料库中已封存文件的单一总数,不会透露其他任何信息,例如这些文件属于哪个根目录或文件夹。这个总数是有意披露的唯一信息:对其保持沉默会误报语料库的规模。
治理由目录数据库执行,而不是寄托于提示词的措辞。
确认地图是否可信
只有当 Agent 知道目录数据库是最新的,搜索结果才有用。
MCP 服务会在运行期间自行维护索引,因此无需安排任务,也无需 Agent 记住这件事。当它无法做到时——索引尚未构建、已过期、刷新失败、无法据以判断的时钟,或索引与文件系统不再一致——每个工具都会在答案上方说明这一点,而 corpus_stats() 会全面比对索引与磁盘。
旧快照绝不会被呈现为完整知识。
专为知识密集型工作打造
| 顾问和咨询师 | 查找以往的分析、提案、框架和交付物,同时避免暴露无关的客户资料。 |
| 研究人员和分析师 | 通过一致的元数据,在大量报告、数据集和原始资料中进行导航。 |
| 产品和战略团队 | 在不同项目和时期之间重新梳理决策、研究、路线图和过往思路。 |
| 工作室和独立从业者 | 将多年积累的工作转化为可复用的上下文,同时掌控客户文件和知识产权。 |
| Agent 构建者 | 为本地 Agent 提供由 MCP 支持的受治理发现层。 |
工作原理
- 将 Shelfmark 指向你现有的文件夹。 可以指定一个或多个根目录。文件仍保留在原处。
- 构建本地目录。 它会遍历获准的根目录,提取可用元数据,应用分类规则,并将引用写入 SQLite。
- 审核归属和保密性。
shelfmark review会询问一些关于你自己文件夹的问题,并将答案写入配置。 - 连接兼容 MCP 的 Agent。 它通过结构化工具搜索、浏览和检查目录记录。
- 仅检索重要内容。 Agent 会先找出相关资料,然后才进行单独的内容访问。
安装
uv tool install shelfmark # or: pipx install shelfmark
# from a checkout:
uv tool install /path/to/shelfmark
Python ≥ 3.11。macOS、Linux 和 Windows——完整套件在 CI 中可在这三个平台上运行,包括 Windows 特有的行为(OneDrive 占位符检测、拒绝根目录边界处的联接)。
Codex 插件
将 MCP 服务及其目录管理、上下文查找和档案研究技能作为一个版本化插件安装:
codex plugin marketplace add Dankaro-projects/shelfmark
codex plugin add shelfmark@shelfmark
然后运行 uvx shelfmark init,再运行 uvx shelfmark refresh,创建本地目录。该插件会通过 stdio 启动固定版本的 Shelfmark MCP 服务;文档路径和元数据会保留在本地计算机上。
如需在不配置 MCP 的情况下安装这三个配套技能:
pnpm dlx skills add Dankaro-projects/shelfmark --full-depth
电子邮件摄取是可选的,所需的额外依赖取决于你的文件格式。.msg 在所有平台上都能解析为 wheels;.pst 需要 libpff-python,它不发布 wheels,且需要从 C 源代码编译,因此需要构建工具链(Visual C++ Build Tools、build-essential 或 Xcode 命令行工具):
uv tool install "shelfmark[msg]" # .msg — no compiler needed
uv tool install "shelfmark[pst]" # .pst — compiles from C source
uv tool install "shelfmark[email]" # both
快速入门
只需运行三个命令,init 就会帮你查找文档——如果默认根目录未找到文档,它会扫描并找出实际存放文档的文件夹,只需按一次键即可修正配置:
$ shelfmark init
Wrote ~/.config/shelfmark/config.toml
WARNING: ~/Documents does not exist on this machine.
These folders do hold documents:
1. ~/Paperwork (~6 document files)
2. ~/Downloads (~1 document file)
Index which folder? [1] — a number, a path, or 'k' to keep ~/Documents:
Root set to ~/Paperwork. Edit ~/.config/shelfmark/config.toml any time.
$ shelfmark refresh
cataloguing ~/Paperwork -> ~/.local/share/shelfmark/catalog.db
seen 6 new 6 updated 0 unchanged 0 rematerialised 0
evicted 0 corrupt 0 restricted 0
$ claude mcp add shelfmark -s user -- shelfmark-mcp
完成安装并连接后,Agent 的首次调用如下所示(这是对上述语料库调用 corpus_stats() 得到的真实结果):
# shelfmark corpus
6 files · 0.0 GB
✓ index fresh — matches disk, last refresh 0 min ago
## Roots
root files own+shareable
Clients 4 0
Decks 2 0
## Rights × confidential
REFERENCE may leave 5
UNKNOWN unreviewed → held 1
## doc_type (what files ARE)
report 4, deck 2
建议额外花五分钟运行 shelfmark review:它会询问一些关于最大未分类子树的问题,并将答案写入配置,从而不再将权利状态标记为 UNKNOWN;shelfmark stats 可随时打印统计信息;shelfmark config 会按检查顺序显示每条权利规则,以及当前匹配的文件数量。
然后在会话中调用:corpus_stats() 了解概况,调用 browse_folder() 浏览,调用 search_docs() / get_file() 查找和检查。
MCP 工具
| 工具 | 能回答的问题 |
|---|---|
corpus_stats() | 整体上有哪些内容,以及如实显示新鲜度。首先调用。 |
browse_folder(prefix) | 文件夹内有什么:数量、大小、方面分布。 |
search_docs(query, …) | 带方面筛选的元数据全文搜索。 |
get_file(path) | 单个文件的完整记录:权利、作者身份、幻灯片标题、重复副本、磁盘状态。 |
search_emails(query, …) | 对已摄取的 .pst/.msg 电子邮件档案进行全文搜索(可选)。 |
结果列表始终会注明截断情况(showing 100 of 195 …);未知筛选值会作为无效筛选项报告并附上建议(绝不会显示为空语料库);摘录会标明截断位置。
配置
所有特定于语料库的内容都放在 config.toml 中——代码仅附带通用默认值。解析顺序:--config 标志 →
$SHELFMARK_CONFIG → ~/.config/shelfmark/config.toml。完整的注释参考请参阅
config.example.toml。重点如下:
| 部分 | 控制内容 |
|---|---|
[[roots]] | 要编制索引的目录树。一个未标记的主根目录;其他根目录会添加标签前缀。 |
[index] | SQLite 目录数据库的存放位置。必须位于所有已编制索引的根目录之外,且不能位于云同步文件夹中——这是一个会被修改的二进制数据库,并且系统会强制执行此要求。 |
[privacy] | 用于检测机密信息和私有子树的正则表达式 → RESTRICTED。内置规则已涵盖 .env、密钥/证书文件、id_rsa、备份代码和身份证明文件。 |
[authors] | 用于匹配你自己的姓名/公司、客户作者和生成器工具的正则表达式——根据 OOXML 作者信息进行 OWN/REFERENCE 分类。 |
[rights] | 用于双轴模型的路径前缀规则:rights(我是否可以重复使用)× confidential(它是否可以离开)。 |
[facets] | 哪些顶层文件夹算作工作/个人文件夹;客户/项目名称位于路径中的哪个位置。 |
[doc_types] / [context_types] | 额外的文件名/文件夹规则,会先于内置的双语(EN/ES)默认规则进行检查;可按名称禁用内置规则。 |
shareable_only 的含义
shareable_only=True 表示已明确分类为可共享:confidential=0 AND rights IN (OWN, REFERENCE)。未经审核的文件会被排除——未经审核并不等于已获准共享。
设置权利分类:shelfmark review
大多数文件都没有 OOXML 作者信息——语料库主要由 PDF、Markdown 和
图像组成——因此作者信息几乎无法用于分类,只有路径规则能够做到这一点。这些规则初始为空,所以新建的目录数据库中的文件大多为 UNKNOWN,而 shareable_only 几乎不会返回任何结果。
这些知识属于你,而不是语料库,因此 review 会询问你——优先处理收益最大的项目,并在屏幕上显示目录数据库已知的信息:
[1/8] Projects
493 unclassified of 662 files · folder reads as: pitch
is: note 202, document 89, code 88, pdf 51
authored by: R. Okonjo (31), A. Lindqvist (12), openpyxl (9)
e.g. programme-overview.pptx · phase-two-proposal.pptx
answer [skip]:
回答 own、own-private、reference、client、personal、scratch
或 skip。对于约 1,900 个文件的语料库,回答五个问题即可确定 80% 的
未分类文件,回答八个即可确定 90%。
shelfmark review # dry run — shows what it would write
shelfmark review --apply # writes the prefixes, re-derives rights
你随时可以停止;它只会询问仍未分类的子树(因此再次运行时会从上次停止处继续),并且写入
配置文件,绝不在数据中写入权利值——因此,只需编辑配置并重新运行 shelfmark rights,即可撤销任何回答。如果作者信息足够集中,能够作为证据,它会提供默认选项;如果作者信息不足,它会保持沉默,而不是猜测。
编辑分类规则
两件人们容易踩坑的事,都是吃过亏才学到的:
-
短正则备选项的两端都要加锚点。 未加锚点的
rfi会匹配 "Dockerfile" 中的部分内容,以及西班牙语单词 "perfil"。添加或删除备选项之前,请先列出它实际匹配的文件名。 -
编辑规则不会重新标记现有文件。 构建器采用增量方式,因此每次更改规则后都要运行:
shelfmark reclassify all # dry run — shows what would change shelfmark reclassify all --apply不要为此使用
build --rebuild:它会重新遍历所有内容(在云同步目录树中,还会把每个已逐出本地的文件都重新下载),而这些文件名已经在数据库中。
保持最新
shelfmark refresh # build + rights + prune + assertions
shelfmark refresh --if-needed # only if a write landed or the index is old
refresh 根据(大小、修改时间、云端驻留状态)增量更新——对于约 3 万个文件的目录树,只需几秒钟——并且每次运行都会断言自身正确性:遍历覆盖情况(操作系统拒绝访问的遍历不能被视为成功)、对已删除文件进行有防护的清理(如果看起来像是大规模删除或根目录未挂载,则拒绝清理),以及治理不变量(搜索索引中没有 RESTRICTED 条目、没有未封存的机密信息、没有不可搜索的非 RESTRICTED 条目)。结果写入数据库旁边的 REFRESH_STATUS.json,MCP 服务会在每次 corpus_stats() 调用时报告这些结果。
任何拒绝修改索引的保护措施都会在 stderr 中说明原因,并以非零状态码退出——你绝不会看到看似正常、却掩盖了拒绝操作的摘要。
当出了问题,却没有任何提示
shelfmark doctor
检查设置中那些悄无声息的故障:目录位于云同步文件夹中(同步客户端每次写入都会上传;若同步客户端在不同时间复制 .db、-wal 和 -shm,恢复时就会得到一个损坏的数据库,它虽然能打开并响应,结果却是错误的);进程无法读取的根目录——在 macOS 上通常是“完全磁盘访问权限”,空遍历正是它掩盖的问题——不可写的数据库文件夹、大部分内容已从云端逐出以及这会造成的影响,还有目录中存在但没有安装读取器的任何邮件格式。
它会指出解决办法,而不是故障;打印每个判断背后的证据,供你推翻判断;并且遇到致命问题时以非零状态码退出,因此可以用于拦截设置脚本。
没有任何标志会改变检查内容。唯一的标志只会改变输出面向的对象:
shelfmark doctor --report
以 JSON 格式输出相同的判断,但不包含语料库内容——只有计数、状态和判定代码,绝不包含路径、文件名、根目录标签或配置字符串。
因为这里没有任何内容会主动联系外部,机器上的损坏目录对任何能提供帮助的人来说都是不可见的;这是仅本地工具能提供的唯一沟通渠道,而且大小约为 1.5 KB,足以附在问题报告中。它包含故障连续次数,这正是“出了问题”和“从星期二起就一直出问题,已经运行了 113 次”之间的区别。
完整内容如下,一处也不省略:
{
"shelfmark": "0.4.7", "python": "3.13.13",
"platform": "darwin", "machine": "arm64", "release": "25.5.0",
"roots": {"configured": 3, "readable": 3},
"catalogue": {"files": 32942, "evicted": 0,
"rights": {"OWN": 3933, "REFERENCE": 27818,
"RESTRICTED": 955, "UNKNOWN": 236}},
"status": {"state": "ok", "detail_kind": "clean",
"failing_since": null, "consecutive_failures": 0},
"findings": [{"code": "reader_missing_msg", "status": "warn",
"count": 2318}],
"worst": "warn"
}
脱敏并非需要任何人牢记的约定:报告不会复制任何自由文本,因此之后新增的检查也无法通过它泄露信息;测试还会在根目录名、文件名和配置中植入一段金丝雀字符串,并在它出现在输出中时失败。
当保护机制拦住你时
这两项大小保护机制无法区分“根目录不可读”和“这些文件确实已被删除”——两者看起来都像遍历结果变少了。因此它们会拒绝操作,指出这两种可能,并保持索引不变:
FAILED — walk saw 63/123 catalogued files, below the 80% floor — either the
root was unreadable to this process or that many files really went away.
Index NOT updated; re-run with --force if the deletion was real.
检查究竟是哪一种情况。如果文件确实已经不存在:
shelfmark refresh --force # accept the short walk, prune past the ceiling
--force 会在删除任何内容之前,将目录备份到 catalog.db.bak-preprune。如果只是根目录已卸载或不可读,修复后正常刷新即可——条目仍然存在。
示例:有人重命名了文件夹
这是保护机制要防范的故障,下面展示其完整过程。一个包含 200 个文件的语料库:Clients/Acme 中有 60 份文档,Admin 中有 140 份。以下每一行都是真实输出;只有路径经过了缩短:
1 — 全新构建。
$ shelfmark refresh
cataloguing ~/Paperwork -> ~/.local/share/shelfmark/catalog.db
seen 200 new 200 updated 0 unchanged 0 rematerialised 0
evicted 0 corrupt 0 restricted 0
2 — 将 Clients/Acme 重命名为 Clients/ACME Corp。 没有删除任何内容,但旧名称下所有已编入目录的路径现在都指向空处,而遍历发现了 60 个从未见过的文件:
$ shelfmark refresh
seen 200 new 60 updated 0 unchanged 140 rematerialised 0
prune REFUSED — 60 of 200 rows are no longer on disk, over the 2% ceiling.
Nothing was deleted; the index still lists them. A real deletion, an
unreadable subtree, or an upgrade widening the default skip list all look
like this. Check what went missing, then re-run with --force to accept it.
拒绝操作是正确的选择:从现在开始,重命名和批量删除看起来完全相同。状态现在是 degraded,而不是 ok——本次运行已结束,但目录明确列出了 60 个文件已不存在的条目。
3 — 再运行一次,目录会同时包含两份记录,因此起作用的是覆盖率下限,而不是修剪上限:
$ shelfmark refresh
FAILED — walk saw 200/260 catalogued files, below the 80% floor — the root
was unreadable to this process, that many files really went away, or an
upgrade widened the default skip list. Index NOT updated; re-run with
--force if the deletion or the new skip list is right.
之后每次运行都会继续失败,不会悄然恢复。
4 — 与此同时,Agent 会得到什么。 没有任何工具会假装什么都没发生;警告会放在最前面,并包含连续次数:
⚠ the last refresh FAILED (walk saw 200/260 catalogued files, below the 80%
floor … — 2 consecutive runs since 2026-08-11T08:46:01Z) — answers below
come from the previous snapshot.
showing 3 of 120 match(es) for '"acme"':
- Clients/Acme/acme_note_055.md
note [UNKNOWN]
注意这些路径:它仍然会响应,而且响应仍基于旧名称。这就是为什么警告横幅不可省略。
5 — 查询出了什么问题。
$ shelfmark doctor
[ok ] root (primary) is readable
[ok ] the catalogue is outside any sync folder
[ok ] the corpus is materialised on disk
[FAIL] the last refresh FAILED — 2 consecutive runs since 2026-08-11T08:46:01Z
walk saw 200/260 catalogued files, below the 80% floor — …
-> The refresh already said this on stderr; if you are only seeing it now,
whatever runs it is discarding its output.
2 of 5 checks need attention.
$ echo $?
1
6 — 文件确实已经移动,因此接受这一变更。
$ shelfmark refresh --force
seen 200 new 0 updated 0 unchanged 200 rematerialised 0
--force: accepting coverage 200/260 (below the 80% floor)
--force: pruned 60 rows, over the 2% ceiling (backup: catalog.db.bak-preprune)
7 — 确认,并注意 stats 最后执行的是实际遍历,而不是读取时钟:
$ shelfmark doctor
[ok ] the last refresh completed cleanly
1 of 5 checks need attention.
$ shelfmark stats
✓ index fresh — matches disk, last refresh 0 min ago
剩下的唯一警告是“目录中有 100% 的内容的权利状态为 UNKNOWN”。在运行 shelfmark review 之前,任何语料库都是如此——请参阅设置权利信息。
你不需要安排定时任务
MCP 服务会自行维护索引。它由客户端启动,在整个会话期间运行,并在启动时刷新一次,此后每当 refresh.max_age_seconds 到期时刷新。无需安装任何东西,无需配置计时器,也不用记住命令。
这是因为,相比调度程序,客户端本来就会启动的常驻进程是更好的触发机制:它继承了客户端已有的文件访问权限,而且只会在你提问时运行。如果你通过其他方式维护目录,shelfmark-mcp --no-auto-refresh 会让它保持不变。
编辑器钩子现在是优化手段,而非必要机制——它们能缩短 Agent 写入文件到文件可供搜索之间的延迟,从一个刷新间隔缩短到当前轮次结束。使用 Claude Code 时,在
~/.claude/settings.json 中配置:
{
"hooks": {
"SessionStart": [{"hooks": [{"type": "command",
"command": "shelfmark hook session-start", "timeout": 60}]}],
"PostToolUse": [{"matcher": "Write|Edit|MultiEdit|NotebookEdit",
"hooks": [{"type": "command", "command": "shelfmark mark-dirty"}]}],
"Stop": [{"hooks": [{"type": "command",
"command": "shelfmark hook stop"}]}]
}
}
mark-dirty 仅在写入发生于已索引根目录下时才会放置标记(其他所有写入几乎没有额外开销);hook stop 会在当前轮次结束时处理该标记。hook session-start 会刷新索引,然后检查索引与磁盘是否一致——这与 MCP 服务执行的检查相同——仅在发现问题时通知你和 Agent。
不要将这些命令包装在 >/dev/null 2>&1 中。本页的早期版本曾明确这样教,而这正是目录连续四天悄然出错的原因:刷新报告的所有拒绝和失败都会发送到 stderr,因此重定向会丢掉唯一的错误通知。目录状态正常时,shelfmark hook 不会输出内容,并且始终以 0 退出——无需再隐藏任何内容。
如果你还希望在没有客户端运行时保持索引最新
这只适用于非 MCP 使用场景,或需要在超大目录树上立即进行首次查询的情况。在 Linux 上,使用 systemd --user 计时器运行 shelfmark refresh --if-needed 是一种简洁的做法。
在 macOS 上,障碍在于 TCC,而非 launchd。LaunchAgent 默认无法访问 ~/Documents、~/Desktop 和 ~/Downloads;os.walk 会吞掉错误,因此构建过程只遍历少量文件并以 0 退出——每一层都报告成功,但索引从未更新。为运行作业的解释器授予“完全磁盘访问权限”后,LaunchAgent 就能正常工作。无论如何,覆盖率断言都能捕获未授权的情况:刷新会明确报错,而不是悄悄什么都不索引。
在 Windows 上,用任务计划程序运行相同命令即可达到同等效果;目前还没有经过实测的定时运行配置方案,因此如果你设置了一个,覆盖率断言同样是防止计划任务失效、索引却悄然过期的唯一保障。
内容哈希与重复项
刷新过程绝不会读取文件内容(必须保持快速)。要生成用于检测重复项的哈希:
shelfmark hash # reads every unhashed, non-sensitive file
shelfmark hash --limit 2000 # chip away at it
绝不会打开敏感条目,也不会打开符号链接。在云同步目录树中,会跳过无数据占位文件——读取此类文件会悄然得到空字符串的哈希,导致不同文件看起来相同。大量文件实际下载到本地后,请重新运行。
Shelfmark 有何不同
- 桌面搜索帮助用户查找文件中的文本。
- 文档管理系统要求将文档导入并在新的环境中管理。
- 检索系统会解析、分块并复制文档内容到搜索索引或向量存储中。
- 文件系统工具让 Agent 直接访问文件,并将所有发现决策都交给 Agent。
Shelfmark 位于另一个层面:它是你现有文档的本地、受管控、基于元数据的地图,专为有选择地提供 Agent 上下文而构建。
Shelfmark 不打算做什么
它不是文档管理系统,不会取代你的文件系统,不需要嵌入向量,不会重新整理你的文件夹,也不会声称自己理解从未读取过的内容。此外,它不会假设每份可发现的文档都可以安全分享。
它为 Agent 提供更好的起点。
这些是经过权衡的决定,不是待办事项:
- 不使用嵌入向量。 优先使用元数据全文检索和分面筛选。
- 不提取内容。 正文文本按设计不会进入索引。
根据实际的未命中情况做决定
应当根据实际证据重新审视这两项坚持,而不是根据竞争对手的功能列表。因此,shelfmark 会在本地记录未找到结果的搜索,并告诉你这些记录意味着什么:
shelfmark misses # the evidence
shelfmark misses --clear # forget it
报告回答了一个问题:元数据搜索是否有可能找到它? 如果某个词语没有出现在任何文件名、路径、作者、标题或幻灯片标题中,那么无论搜索措辞如何,它都无法被找到——这正是内容提取要解决的问题。如果某个词语确实出现在你的语料中却仍未找到,那就是措辞或筛选条件的问题,需要采用不同的修复方式。没有这一区分,未命中日志只能证明有人在搜索。
73 searches returned nothing (2026-06-02 → 2026-08-07)
Most-missed terms:
9 abatement
7 timeline (nowhere in your metadata)
6 commitments (nowhere in your metadata)
41 of 62 distinct terms (66%) appear nowhere in your filenames,
paths, authors, titles or slide titles.
These are mostly things metadata search could NEVER have found, however
phrased. That is the pattern the README says should reopen content
extraction — not embeddings.
错误的筛选条件、不可能的年份范围以及针对过期索引的搜索不会被记录为未命中:这些情况已经向调用方作出了解释,记录它们只会掩盖真正的信号。记录仅保存在本地,有数量上限,也绝不会离开你的设备。使用 [misses] enabled = false 将其关闭——记录你自己的搜索词会影响隐私,而这是否可以接受,确实取决于具体语料。
常见问题
Shelfmark 会上传我的文档吗? 不会。它在本地运行并建立本地目录,不会将文档内容复制到目录中。
目录包含什么? 文件引用和派生元数据:路径、文件名、类型、大小、日期、作者、分类、选定的 Office 属性、演示文稿标题,以及可选的内容哈希。
它会读取文档内容吗?
文件目录不会为正文建立索引。只有两种有意设置的例外:shelfmark hash 会打开文件以计算内容哈希,用于检测重复项;可选的电子邮件导入可以为邮件正文建立索引,以便 search_emails 正常工作。两者都是选择启用的命令,都会跳过符号链接,并遵循你的隐私规则——与机密/私密模式匹配的文件或归档绝不会被任一命令打开。
我需要先重新整理文件夹吗? 不需要。更好的文件名和元数据有助于改进分类,但不要求采用正式的目录结构。
它能理解所有命名不当的文件吗? 不能。没有有意义的文件名、也没有嵌入元数据的文件,如果不读取内容就很难识别。Shelfmark 只说明自己知道什么,不会猜测。
为什么不直接让 Agent 访问文件系统? 文件系统访问让 Agent 可以打开文件。Shelfmark 则帮助它决定哪些文件值得打开,以及哪些文件应当完全排除在结果之外。
为什么不使用向量数据库? 向量数据库用于从内容中进行语义检索。Shelfmark 解决的是更早一步的问题:先发现并管理现有内容,再决定究竟要处理哪些内容。两者可以兼容;Shelfmark 先行。
哪些 Agent 可以使用它?
任何支持本地 MCP 服务的客户端都可以使用。使用 claude mcp add shelfmark -s user -- shelfmark-mcp 注册,或将客户端指向 shelfmark-mcp 命令。
开发
uv run --group dev pytest # the suite ships in the sdist, so this
# runs from the release artifact too
测试语料(包括 OOXML 文件)会在每次运行时生成;不会提交任何二进制测试文件,也不会读取任何真实文档。
许可证
MIT——详见 LICENSE。可免费使用、修改和再分发。
mcp-name: io.github.Dankaro-projects/shelfmark
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。