关于 Keepfor.me
Keepfor.me 是面向需要保存和整理网页及笔记,并向 AI Agent 开放内容的稍后阅读型个人图书馆。它结合持久网页快照与关键词、语义混合搜索。
主要功能
- 01保存网页、笔记和片段
- 02关键词与语义混合搜索
- 03保存原始及清理后的快照
- 04管理标签、高亮和集合
- 05从回收站恢复条目
适用场景
- 01保存文章以便稍后阅读
- 02按主题搜索个人图书馆
- 03让 Agent 阅读和整理已保存内容
- 04恢复误删的图书馆条目
MCP 相关
在 `/api/mcp` 提供远程、无状态的 Streamable HTTP MCP 服务,并通过 Bearer Personal Access Token 验证。其工具支持保存、搜索、阅读、标记和管理图书馆条目。
配置
远程服务无需本地安装。选择你使用的客户端,复制配置或使用一键安装。
https://app.keepfor.me/api/mcpclaude mcp add --transport http melcutz-keepfor-me https://app.keepfor.me/api/mcp{"mcpServers":{"melcutz-keepfor-me":{"type":"http","url":"https://app.keepfor.me/api/mcp"}}}在 Cursor 中打开{"servers":{"melcutz-keepfor-me":{"type":"http","url":"https://app.keepfor.me/api/mcp"}}}在 VS Code 中打开{"mcpServers":{"melcutz-keepfor-me":{"type":"http","url":"https://app.keepfor.me/api/mcp"}}}{"mcpServers":{"melcutz-keepfor-me":{"serverUrl":"https://app.keepfor.me/api/mcp"}}}命令在终端中运行;JSON 粘贴到对应客户端的 MCP 配置后重启。需要授权的服务会在首次连接时提示登录。
https://app.keepfor.me/api/mcp需要授权工具
详细介绍
Keepfor.me
一款轻量、快速的稍后阅读和个人资料库应用,完全运行于 Cloudflare Python Workers、使用 FTS5 的 D1 SQLite、Vectorize、R2 和 Model Context Protocol(MCP)。
功能
- 四项核心任务:与 AI Agent 一起捕获、存储、搜索和分享。
- Python 边缘运行时:由 Cloudflare Python Workers(Pyodide)上的 FastAPI 提供支持。
- 可安装的 PWA 和原生分享:可在 Android 和 iOS 上添加到主屏幕。在 Android 上,原生 Web Share Target 可直接集成到系统分享面板中。在 iOS 上,只需轻点 1 次,Apple Shortcuts 即可使用你的 Safari 会话集成到系统分享面板中,并配合 PWA 中的智能剪贴板检测。
- 无干扰阅读器:可自定义主题(浅色、复古纸张色、深色)、字体(无衬线、衬线、等宽)和字号。
- 混合搜索:通过倒数排名融合(RRF),结合 D1 FTS5 BM25 关键词匹配和 Vectorize 语义嵌入(
bge-base-en-v1.5)。FTS 和嵌入调用并发运行,且向量检索路径设有时间限制,因此 AI 绑定运行缓慢时会降级为仅关键词搜索,而不会一直卡住。 - 可靠的内容提取:使用
trafilatura自动解析正文,并在失败时回退到 OpenGraph 元数据,确保书签不会丢失。与浏览器一致的请求标头,以及可选的阅读器代理回退(通过READER_PROXY_BASE配置,默认关闭),可访问许多设有机器人防护墙的来源(HTTP 403)。 - 清晰可见的提取状态:每个条目都会显示
Extracting…、内容或失败原因(例如HTTP 403),而不是悄无声息地卡住。批量导入会通过队列分发,不会阻塞请求。 - R2 中的双快照:原始 HTML 快照(
raw.html)和经过净化的阅读器 HTML(clean.html)。 - 移至回收站,而非丢失数据:从界面或 Agent 删除条目时,会将其移至回收站,而非彻底销毁。条目行、快照和向量都会保留,因此可立即恢复;在回收站视图中选择永久删除是唯一的永久删除方式,定时清理任务会在 30 天后清空回收站。
- 零配置单租户锁定:首次注册会自动获得管理员所有权,并禁止外部用户注册。
- Model Context Protocol(MCP):位于
/api/mcp的远程 Streamable HTTP 服务(无状态,使用 Bearer PAT 进行身份验证),可供 opencode、Claude Desktop 和 Cursor 使用,无需本地安装。 - 即时浏览器捕获:可拖放的 JavaScript 书签工具,以及未打包的 Manifest V3 扩展。
- 导入与导出:导入 Pocket、Instapaper、Omnivore 和浏览器书签导出文件(Netscape HTML 和 CSV)。保留 Chrome 文件夹名称作为标签。可从 Google Keep Takeout 压缩包、Evernote
.enex文件和Markdown/文本文件压缩包(Obsidian vault、通过 Exporter 导出的 Apple Notes、Simplenote)导入笔记:保留标签和原始日期,笔记中的链接会作为普通保存项处理,附件会列出但不会存储,重复导入不会产生重复项(压缩包和.enex的上限为 50 MB,HTML/CSV 的上限为 10 MB;压缩包内每个文件上限为 2 MB,总计上限为 200 MB、最多 20,000 个文件,因此会拒绝解压缩炸弹)。每次笔记上传都会保留一条状态记录,记录进度和最终计数(导入的笔记数、跳过的重复项、排队的链接、未导入的附件、解析错误、因计划限制跳过的项目),并保存数量受限的错误消息列表,因此刷新后仍可查看结果。 - 移动设备优先的界面:手机上提供底部标签栏和标签条,触控目标 ≥32px,在宽度低至 360px 时也不会出现水平溢出。
架构概览
flowchart LR
Browser["Web UI / PWA / Share Sheet"] -->|FastAPI| Worker["Cloudflare Python Worker"]
Claude["Claude Desktop / MCP Agents"] -->|Streamable HTTP + Bearer PAT| Worker
Worker -->|Queries & FTS5| D1[("D1 Database")]
Worker -->|Enqueue job| Queue["Cloudflare Queue"]
Queue -->|Process batch| Worker
Worker -->|Raw & Clean HTML| R2[("R2 Storage")]
Worker -->|Embeddings| WAI["Workers AI"]
Worker -->|Upsert vectors| Vec[("Vectorize")]
提取生命周期
保存 URL 会插入一条 status='queued' 的记录并将任务加入队列;队列消费者会获取内容、提取内容、存储快照,并将状态更新为 ok 或 failed,同时记录失败原因。失败状态始终会显示在 UI 和阅读器中。
两项经过深思熟虑的设计选择:
- 未绑定队列 → 内联提取。 本地开发和测试环境没有队列消费者,因此
save_item会同步提取,而不会让条目永远卡在queued状态。 - 批量导入绝不内联执行。 导入 900 个书签会在一次请求中产生数千次 D1 写入;因此,系统会将任务拆分为每批 25 个书签的队列消息,或者在未绑定队列时使用后台任务。
如果消息丢失,可以通过 Settings → Re-queue stuck items 为卡在 queued 状态的记录重新发送任务(跳过刚加入的记录,以免重复发送仍在处理的消息)。
回收站与恢复
删除不会销毁数据。MCP delete_item 和 DELETE /api/items/{id} 会设置 items.deleted_at,仅此而已:记录、R2 快照和向量嵌入都会保留,因此 Agent 无法销毁知识库数据。已移至回收站的条目在恢复之前不会显示在任何地方(知识库、搜索、MCP 读取、计数、导出)。
回收站视图(/trash)会列出这些条目,并提供两个操作:Restore 可将条目恢复到知识库,Delete forever 是唯一的永久删除方式。主机也可以自行运行定期清理任务:
from keepfor.models.items import purge_trash
# Hard-deletes trashed items older than 30 days (row, snapshots, vectors).
await purge_trash(db, env, user_id=None, older_than_days=30)
# A multi-tenant host passes the tenant scope: it carries the R2 prefix
# and the Vectorize namespace of the tenant. An unscoped call is refused.
await purge_trash(db, env, user_id=None, older_than_days=30, scope=scope)
不会在用户不知情的情况下删除任何内容:如果没有调度器,回收站只会不断增长。
阅读器代理回退(READER_PROXY_BASE)
默认情况下,Keepfor.me 不会将获取失败的请求转发给任何第三方服务。当源站拒绝直接获取(HTTP 403、429、530)时,该条目会记录为失败。
如需启用阅读器代理回退(例如使用 Jina Reader 或可自托管的 readability 代理):
- 在
wrangler.jsonc中配置READER_PROXY_BASE(vars.READER_PROXY_BASE = "https://r.jina.ai/"),或在.dev.vars中配置。 - 隐私声明:启用后,被拦截文章的 URL 会传输到配置的代理服务。根据 SSRF 出站策略,严格禁止访问私有 IP 范围(例如
127.0.0.1、10.0.0.0/8、192.168.0.0/16)和本地域名,这些地址永远不会转发给任何阅读器代理。
有关私有部署、离线部署和出站策略的完整指南,请参阅 docs/self-hosting.md。
部署
1. 前置条件
- Node.js 20 或更高版本以及 npm
- uv 0.12.3 或更高版本
- 已启用 Workers、D1、R2、Vectorize 和 Queues 的 Cloudflare 账户
- 自定义域名:
keepfor.me(或标准的*.workers.dev子域名)
2. 配置 Cloudflare 资源
运行以下命令一次,以创建 D1 数据库、R2 存储桶、Vectorize 索引和队列:
# 1. Create D1 Database
npx wrangler d1 create keepfor-me-db
# Copy the returned database_id into wrangler.jsonc
# 2. Create R2 Bucket
npx wrangler r2 bucket create keepfor-me-bucket
# 3. Create Vectorize Index (768 dimensions for bge-base-en-v1.5)
npx wrangler vectorize create keepfor-me-index --dimensions=768 --metric=cosine
# 4. Create Queue
npx wrangler queues create keepfor-me-queue
3. 应用数据库迁移
应用初始架构(表、FTS5 虚拟表、索引):
npx wrangler d1 migrations apply keepfor-me-db --remote
本地测试时:
npx wrangler d1 migrations apply keepfor-me-db --local
4. 部署
部署需手动进行:自托管用户通过 pywrangler deploy 或 GitHub Actions workflow_dispatch(输入 confirm: "deploy")部署到自己的 Cloudflare 账户。推送到 main 不会自动部署。
警告
D1、R2 和 Vectorize 绑定在
wrangler.jsonc中设置了"remote": true。切勿使用wrangler.jsonc运行本地开发,因为它会直接与远程资源交互。若要进行本地模拟,请将wrangler.local.example.jsonc复制为wrangler.local.jsonc,然后运行uvx --from workers-py pywrangler dev --config wrangler.local.jsonc。
PyWrangler 会打包 pyproject.toml 中声明的 Python 依赖。
# Always dry-run first and check the "Total (N modules)" row: the deploy
# hard-fails if the bundle exceeds 64 MiB.
uvx --from workers-py pywrangler deploy --dry-run
uvx --from workers-py pywrangler deploy
健康的打包文件大约为 52,000 KiB / ~4,500 个模块。如果模块表中出现 .venv-workers/ 路径,说明构建虚拟环境正在被打包,部署距离失败只差一次依赖升级。
部署完成后,访问你的域名(例如 https://app.keepfor.me 或
https://keepfor-me.<your-subdomain>.workers.dev)。第一个注册的用户会自动成为管理员,并关闭公开注册。
5. 自定义域名
wrangler.jsonc 声明了 app.keepfor.me 的路由。创建一次 DNS 记录(Cloudflare 控制面板 → DNS → 添加记录):
| 类型 | 名称 | 内容 | 代理 |
|---|---|---|---|
A | app | 192.0.2.1 | 已代理(橙色云朵) |
Worker 路由会在访问 IP 之前匹配,因此占位地址可以正常使用。workers_dev 保持启用状态,因此 *.workers.dev URL 仍可使用。
连接 AI Agent(远程 MCP)
Keepfor.me 在 https://app.keepfor.me/api/mcp 托管了一个远程 Streamable HTTP MCP 服务,提供 42 个工具,用于捕获、查找、阅读和标记你的资料库:
save_url:将任意网页保存到你的资料库。save_urls:一次调用即可将多个网页保存到你的资料库,并为每个 URL 返回状态。save_note:保存笔记、片段或提示词模板。search_library:跨文本内容和含义进行混合搜索。search_passages:搜索段落,并提供永久快照 URL。find_item:按标题或 URL 查找项目 ID(不搜索,也不使用嵌入)。get_item:按 ID、URL 或精确标题读取元数据和摘要。read_item:分页读取全文。list_items:使用排序和筛选条件分页浏览精简内容。reading_history:查看你保存和打开过的内容,并可按日期筛选。related_items:查找与指定项目或文本相似的项目。list_tags/create_tag/describe_tag/update_tag/rename_tag/merge_tags/delete_tag:完整的标签管理功能。tag_item/tag_items/set_item_tags/retag_by_query:安全地添加标签,支持批量操作(默认仅试运行)。bulk_update:更新所有符合筛选条件的项目的状态、置顶状态和标签(默认仅试运行)。suggest_tags/search_tags/tag_stats/related_tags/tag_cleanup:标签词汇工具。find_cleanup:只读的清理报告(重复 URL、失效链接、长期未打开的旧项目、未加标签的项目、近似重复的标签)。delete_item/pin_item:移除或置顶项目。library_digest:精简摘要 — 保存内容、打开内容、热门标签和未读重点。create_highlight/list_highlights/delete_highlight:引用项目中的段落,并通过字符偏移定位。create_collection/list_collections/add_to_collection/remove_from_collection/rename_collection/delete_collection:用于归纳资料库项目的命名集合。get_account:查看你的套餐、试用结束时间和剩余配额 — 此工具免费,因此即使配额用尽仍会响应。
该服务无状态(没有会话,返回普通 JSON 响应,不使用 SSE 流),并使用与 REST API 相同的个人访问令牌进行身份验证。
设置
- 在 Keepfor.me 界面中前往 设置与 API。
- 点击 生成令牌,创建个人访问令牌(
kfm_live_...)。如果 Agent 只需浏览和搜索资料库,请在权限范围选择器中选择 只读。 - 将客户端指向该端点,并在 Bearer 标头中传入令牌:
opencode(opencode.json):
{
"mcp": {
"keepfor-me": {
"type": "remote",
"url": "https://app.keepfor.me/api/mcp",
"headers": { "Authorization": "Bearer {env:KEEPFOR_ME_TOKEN}" }
}
}
}
然后在启动 opencode 前执行 export KEEPFOR_ME_TOKEN=kfm_live_...,并在更改配置后重启 opencode。
**Cursor / Claude Desktop:**添加自定义远程 MCP 服务,URL 为
https://app.keepfor.me/api/mcp,并设置标头
Authorization: Bearer kfm_live_YOUR_TOKEN_HERE。
无需任何客户端即可检查握手:
curl https://app.keepfor.me/api/mcp \
-H 'Content-Type: application/json' \
-H 'Accept: application/json, text/event-stream' \
-H "Authorization: Bearer kfm_live_YOUR_TOKEN_HERE" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
可用的 MCP 工具(服务端 0.2.0)
MCP 服务提供 42 个工具。可一次更改多个项目的工具带有 dry_run 标志:该标志只接受 JSON 布尔值(省略时默认为 true,即预览),其他任何值都会被拒绝,而不会执行更改。
save_url— 将任意网页保存到你的资料库。可选接受agent_summary、why_saved、source_context、saved_by,用于记录 Agent 研究来源。save_urls— 一次调用最多将 50 个网页保存到你的资料库,并为每个 URL 返回状态(queued、exists、invalid、over_limit)和总数。需要写入权限范围。URL 会按规范 URL 去重;每个新 URL 都会经过与save_url相同的保存检查(保存限额拒绝的 URL 会返回over_limit);提取任务会进入队列,因此调用不会等待网页处理完成。save_note— 保存个人笔记、代码片段或提示词模板。可选接受相同的来源记录字段。search_library— 基于文本和含义进行混合搜索(返回带摘要的条目级结果)。search_passages— 在语义分块中进行段落级混合搜索,并支持max_chars字符数预算。返回chunk_id、字符偏移量和永久snapshot_url(R2 存档),即使原链接失效也可访问。read_item— 以节省令牌的方式分页读取完整内容。返回text、next_cursor、total_chunks、outline。get_item— 通过item_id、url或精确的title获取元数据和摘录。默认将content_text限制为摘录,并报告truncated和content_length;使用include_content=true可获取全文。list_items— 简洁地分页浏览条目(id、title、url、saved_at、tags——不含正文)。按saved、opened或title排序;可按since/until、domain、pinned、type和标签筛选。find_item— 按精确/前缀标题或精确/规范化 URL 查找条目 ID。不进行搜索,也不使用嵌入:这是从已知标题获取可读取的item_id的快捷方式。reading_history— 阅读历史记录,支持按日期(since/until)、事件类型(saved/opened)筛选,并可选按查询词/标签筛选。related_items— 根据item_id或原始text,通过向量相似度查找最近邻条目,并附上一行原因说明。list_tags— 列出每个标签及其条目数和最后使用日期。按count、name或recent排序;可按prefix和min_count筛选。create_tag— 创建标签,可选设置description、color和parent(层级结构,例如work/research)。describe_tag— 获取标签的元数据、父级、子级和条目数。update_tag— 编辑标签的描述、颜色或父级。rename_tag— 重命名标签;所有带有该标签的条目都会更新。merge_tags— 将多个标签合并到一个目标标签中(每次调用最多 20 个源标签)。delete_tag— 从条目中移除标签。不会删除条目。默认dry_run=true:返回受影响的数量和样例;dry_run=false时执行操作。tag_item— 为单个条目添加或移除标签。tag_items— 为多个条目添加或移除标签。默认dry_run=true。每次调用最多处理 100 个条目;结果会报告total_matched和truncated。retag_by_query— 为符合标题/URL 查询条件(以及标签筛选条件)的条目添加标签。默认dry_run=true。每次调用最多处理 100 个条目;结果会报告total_matched和truncated。bulk_update— 更新符合筛选条件(状态组、保存日期范围、域名、置顶状态、类型、标签)的所有条目的状态、置顶状态和标签。默认dry_run=true:仅报告total_matched、affected和truncated,不会写入任何更改。每次调用最多更新 200 个条目;空筛选条件或空更改会被拒绝。所有更新都在一个事务中执行,标签名称遵循create_tag规则(由 2–64 个a-z、0-9、空格、_或-字符组成)。set_item_tags— 替换单个条目的全部标签。suggest_tags— 为条目或自由文本推荐标签,并优先从你现有的词汇表中选取。search_tags— 模糊搜索标签名称和描述(machinelearning可找到machine-learning)。tag_stats— 资料库的标签使用统计信息。related_tags— 查找与某标签同时出现在相同条目中的标签。tag_cleanup— 查找值得检查的近似重复标签、未使用标签和未加标签的条目。find_cleanup— 只读清理报告:重复条目(规范化 URL 相同)、失效链接(记录的提取失败——不发送网络请求)、从未打开的旧条目、未加标签的条目和近似重复标签。接受categories、older_than_days和每个类别的limit(最大值为 100);每个类别都会报告候选项、数量和truncated标志。条目类别会为每个条目列出一个 ID 和原因;分组类别会为每个分组列出一项——duplicates会给出该组的 ID(最多 10 个)、组内数量以及 ID 列表是否被截断,near_duplicate_tags会给出该组的标签名称。因此,limit限制的是分组数量,即使同一个 URL 保存了 300 次,响应也依然简洁。delete_item— 将条目移至回收站。条目会离开资料库,但不会被销毁:30 天内仍可恢复,且只有用户能在回收站视图中将其永久删除。pin_item— 将条目置顶或取消置顶。library_digest— 只读回顾一段时间内的资料库情况:保存数、打开数、热门标签和未读精选内容。可选接受since、until和tag;响应中的每个列表都有数量上限。create_highlight— 引用条目中的一段内容,并用该段在content_text中覆盖的字符偏移量进行定位。start_offset默认为0,end_offset默认为start_offset + len(text);如果结束偏移量超出条目内容末尾,则会被拒绝。list_highlights— 列出单个条目的所有高亮内容,按其在文本中的位置排序,并附上引用文本、偏移量、可选备注和颜色。delete_highlight— 移除一个高亮内容。其所属条目不会被删除。create_collection— 创建一个命名集合,用于收纳资料库条目,并可选添加描述。若名称已存在,则原样返回该集合。list_collections— 列出你的集合及其条目数和条目 ID(每个集合最多列出 20 个条目;条目更多时会显示items_truncated)。add_to_collection— 将条目加入集合,可通过名称或 ID 指定集合。每次调用最多处理 100 个条目;结果会报告added、skipped和truncated。remove_from_collection— 将条目从集合中移除。条目本身不会被删除。rename_collection— 重命名集合。若新名称已被占用,则会拒绝操作,不会合并集合。delete_collection— 删除集合及其关联关系。集合中的条目不会被删除;请使用remove_from_collection清空集合。get_account— 只读查看你的账户信息:套餐、trial_ends_at以及每种计量操作的配额(limit、used、remaining、resets_at)。此调用免费,不消耗配额单位,因此即使配额耗尽也能返回结果。 所有工具都声明了 MCP 注解(readOnlyHint、destructiveHint、idempotentHint)。这些是提供给客户端的提示,服务端会用它们来强制执行令牌的作用域(见下文)。令牌不会因缺少作用域信息而被拒绝:没有作用域信息意味着拥有完整访问权限。每个工具的说明都会指出何时应使用该工具,而不是它的同类工具。列表类型的响应包含limit、offset/cursor和fields投影;默认的list_items页面大小保持在 4 KB 以内。
标签筛选器(tags_all、tags_any、tags_none、untagged)适用于 list_items、search_library、search_passages、find_item、reading_history 和 bulk_update,并会统计子标签:筛选 work 也会匹配 work/research。
MCP 资源和提示词
该服务还支持 MCP 规范中的资源和提示词部分,身份验证和租户范围与工具相同。
资源 — 以只读方式查看你自己的知识库:
| URI | 内容 |
|---|---|
keepfor://item/{item_id} | 一篇已保存的文章或笔记:元数据、标签和存储的文本(最多 20,000 个字符;若内容被截断,truncated 会说明)。 |
keepfor://tag/{name} | 你的一个标签及带有该标签的项目(最多 50 个;若还有更多,items_truncated 会说明)。 |
resources/list 会返回你最近的项目和最常用的标签,resources/templates/list 会返回两个 URI 模板,resources/read 会以 JSON 格式返回内容。
提示词 — 可重复使用、用于读取你自己的数据的模板:
| 提示词 | 参数 | 功能 |
|---|---|---|
research_brief | topic(必需)、max_items | 向助手简要介绍你已保存的相关主题内容,让研究会话从你的知识库开始。 |
weekly_review | days、tag | 回顾你过去几天保存的内容,并请助手总结以及推荐接下来阅读的内容。 |
令牌作用域(只读令牌)
个人访问令牌可以限制为只读。在设置与 API 中创建令牌时,使用令牌名称旁边的作用域选择器(默认值为完整访问权限):
*(全部) — 默认值。所有工具均可使用,与之前完全相同。read— 通过 MCP,该令牌在tools/list中只能看到读取工具;任何写入或破坏性工具(save_url、save_note、tag_item、create_tag、update_tag、rename_tag、merge_tags、delete_tag、tag_items、retag_by_query、bulk_update、set_item_tags、delete_item、pin_item、create_highlight、delete_highlight、create_collection、add_to_collection、remove_from_collection、rename_collection、delete_collection)都会被拒绝,并返回 JSON-RPC 错误(error.data.type=insufficient_scope),且拒绝发生在执行之前——不会写入任何内容。
作用域功能推出前创建的令牌仍保留完整访问权限:其存储的作用域为 NULL,表示“全部”。无论令牌用于何处,都会强制执行作用域:通过 MCP 使用时,每个工具都需要其注解声明的作用域;通过 REST 使用时,只读令牌在每个写入路由上都会收到 403。例外是只读 POST 路由 /api/search 和 /search:搜索属于读取操作,因此只读令牌可以使用这些路由。/api/mcp 会按工具强制执行作用域。PAT 完全不能用于 /settings/*,因此令牌永远无法创建权限更广的令牌。
自托管用户:作用域列来自迁移文件 0012_pat_scopes.sql,因此请在部署前应用迁移(上文第 3 步)。
浏览器与移动设备捕获
1. Android(PWA + 系统分享面板)
- 在 Chrome 中打开你的域名,然后点按添加到主屏幕(或安装应用)。
- 如需在任何应用中保存内容,点按分享 → Keepfor.me — 原生 Web Share Target 会打开预填好的保存面板;向后滑动即可无缝返回你的应用。
2. iPhone 和 iPad(Apple 快捷指令与智能剪贴板)
iOS WebKit 会将 PWA 与系统分享菜单隔离。Keepfor.me 为 iOS 提供两种无需繁琐操作的保存方式:
- 一键 Apple 快捷指令(分享菜单):前往 设置与 API → iPhone 和 iPad 快速分享,然后点按 安装 Keepfor.me 快捷指令。这会在 iOS 分享菜单中添加一个原生操作,使用你现有的 Safari 登录会话直接将 URL 发送到 Keepfor.me(无需 API 令牌或技术配置)。
- PWA 智能剪贴板:在 Safari 中将应用添加到主屏幕(分享 → 添加到主屏幕)。在任意应用中复制链接,然后切换到 Keepfor.me PWA 时,浮动提示会检测剪贴板中的 URL,让你轻点一下即可保存。
3. 拖放书签小程序(桌面端)
前往 设置,将 Keepfor.me 按钮拖到浏览器的书签栏。点击它即可在任意页面打开快速保存对话框。
书签小程序始终显示通用地球图标。若要使用应用图标:为你的域名下任意页面添加书签,编辑该书签,将复制的代码(设置 → 复制代码)粘贴为其 URL,并将名称设为
Keepfor.me。
4. 浏览器扩展(Manifest V3)
- 打开 Chrome/Brave/Edge,并前往
chrome://extensions/。 - 启用 开发者模式(右上角)。
- 点击 加载已解压的扩展程序,然后选择
keepfor.me/browser-extension文件夹。 - 点击扩展图标,在设置中(
⚙️)输入一次 Worker URL 和 PAT 令牌,然后按Alt+S保存任意标签页。
本地开发与测试
安装项目及其测试依赖,然后运行 pytest 测试套件:
python3 -m pip install -e . pytest pytest-asyncio pytest-cov httpx
python3 -m pytest tests/ -q
请从仓库根目录运行 pytest——只有当根目录位于 sys.path 中时,keepfor 才能作为命名空间包解析。
按照 CI 的确切方式进行 lint 和格式化(单独运行 ruff check . 会使用不同的规则,并且会通过 CI 会拒绝的内容):
ruff check keepfor/ tests/ --select=E,W,F,I,N
ruff format --check keepfor/ tests/
使用其 Python 依赖项运行本地 Worker 预览:
uvx --from workers-py pywrangler dev
本地开发环境没有队列消费者,因此保存的链接会直接提取内容,并立即显示。
性能说明
冷启动是影响 Python Workers 感知延迟的主要因素:页面在预热时加载约需 250ms,但隔离实例启动时可能超过 1.5s。
- 导入依赖图维护:大型文章解析器(
trafilatura、bs4、lxml)会在提取代码中按需导入。回归测试会确保它们不会进入请求导入路径。 - 并发搜索与 I/O:混合搜索会并发运行 D1 FTS 和 Workers AI 嵌入向量查询(
asyncio.gather),并受 5s 超时限制。 - D1 查询优化:复合索引(
(user_id, created_at DESC)和(user_id, status, created_at DESC))可消除 SQLite 内存临时 B-tree 排序文件(USE TEMP B-TREE FOR ORDER BY)。状态计数、信息流条目和标签聚合通过asyncio.gather并行执行;多语句标签和删除变更则通过execute_batch批量处理,以尽量减少 RPC 往返。
支持本项目
如果你觉得 Keepfor 很有用,可以支持其开发:
在 Stripe 上捐款支持 Keepfor(预设金额为 $5,也可自行选择金额)。
参与贡献
欢迎贡献!请阅读 CONTRIBUTING.md,了解开发工作流、测试和拉取请求流程的详情。
所有贡献者都必须签署我们的 贡献者许可协议(CLA)。你仍保留作品的版权所有权;该协议授予我们运营托管服务和提供商业许可证所需的许可。
许可证
Keepfor.me 采用 GNU Affero General Public License v3.0(AGPL-3.0-only)授权。
- 自托管:你可以不受限制地将其自托管,用于个人或组织用途。
- 网络服务:如果你修改 Keepfor.me 并通过网络将其作为服务提供,则必须根据 AGPL-3.0 发布修改后的源代码。
- 商业授权:可应要求提供商业许可证和其他许可协议,以支持专有、企业或闭源部署。详情请参阅 LICENSING.md。
- 此前的版本:许可证变更之前发布的版本仍采用 MIT License,已取得这些版本的用户可继续使用。
- 商标:项目名称和标志受 TRADEMARK.md 约束。
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。