mcpField
O
ofw-mcp

ofw-mcp

本地服务
github.com/chrischall生产力14今天更新

关于 ofw-mcp

ofw-mcp 将 Claude 连接到 OurFamilyWizard 账户,以访问共同育儿消息、日历事件、费用和日志。它提供读写工具,并通过可配置的写入模式和确认机制控制操作;项目说明其代码未经人工审计。

主要功能

  1. 01读取消息、日历事件、费用和日志
  2. 02创建或管理草稿、事件、费用和日志条目
  3. 03缓存消息数据并报告时效状态
  4. 04下载消息附件并上传文件
  5. 05通过写入模式和确认机制限制操作

适用场景

  1. 01查看近期共同育儿消息和未读数量
  2. 02检查即将到来的家庭日历事件
  3. 03汇总费用和报销状态
  4. 04起草回复或记录日志条目

MCP 相关

ofw-mcp 是 MCP 服务,向 Claude 提供 OurFamilyWizard 账户数据和操作。读取与写入工具采用不同的审批方式,部分操作由服务端确认。

配置

本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。

mcp.json · 通用配置
{
"mcpServers": {
"chrischall-ofw": {
"command": "npx",
"args": [
"-y",
"ofw-mcp"
],
"env": {
"OFW_USERNAME": "<OFW_USERNAME>",
"OFW_PASSWORD": "<OFW_PASSWORD>",
"OFW_WRITE_MODE": "<OFW_WRITE_MODE>",
"OFW_CALENDAR_WRITES": "<OFW_CALENDAR_WRITES>",
"OFW_ALLOW_MARK_READ": "<OFW_ALLOW_MARK_READ>",
"OFW_FETCH_UNREAD_BODIES": "<OFW_FETCH_UNREAD_BODIES>",
"OFW_READ_DAYS": "<OFW_READ_DAYS>",
"OFW_NO_READ_DAYS": "<OFW_NO_READ_DAYS>",
"OFW_SESSION_CACHE": "<OFW_SESSION_CACHE>",
"OFW_SESSION_FILE": "<OFW_SESSION_FILE>",
"OFW_EXPENSE_ONLY": "<OFW_EXPENSE_ONLY>",
"OFW_EXPENSE_UPLOAD_ONLY": "<OFW_EXPENSE_UPLOAD_ONLY>",
"OFW_INLINE_ATTACHMENTS": "<OFW_INLINE_ATTACHMENTS>",
"OFW_ATTACHMENTS_DIR": "<OFW_ATTACHMENTS_DIR>",
"OFW_UPLOAD_DIR": "<OFW_UPLOAD_DIR>",
"OFW_CACHE_IDENTITY": "<OFW_CACHE_IDENTITY>",
"OFW_CACHE_DIR": "<OFW_CACHE_DIR>",
"OFW_DISABLE_FETCHPROXY": "<OFW_DISABLE_FETCHPROXY>",
"OFW_AUTO_REFRESH": "<OFW_AUTO_REFRESH>",
"OFW_FRESHNESS_TTL_SECONDS": "<OFW_FRESHNESS_TTL_SECONDS>",
"OFW_SYNC_MAX_REQUESTS": "<OFW_SYNC_MAX_REQUESTS>",
"OFW_REQUEST_TIMEOUT_MS": "<OFW_REQUEST_TIMEOUT_MS>",
"OFW_DEBUG_LOG": "<OFW_DEBUG_LOG>",
"DISPLAY_TZ": "<DISPLAY_TZ>",
"FETCHPROXY_WS_PORT": "<FETCHPROXY_WS_PORT>",
"FETCHPROXY_WS_HOST": "<FETCHPROXY_WS_HOST>",
"FETCHPROXY_IDENTITY_DIR": "<FETCHPROXY_IDENTITY_DIR>"
}
}
}
}
启动命令npx -y ofw-mcp
需要配置的环境变量
OFW_USERNAMEYour OurFamilyWizard login email address. Optional — if omitted, the server falls back to the fetchproxy browser extension (requires being signed in to ourfamilywizard.com).
OFW_PASSWORD密钥Your OurFamilyWizard password. Optional — see OFW_USERNAME.
OFW_WRITE_MODEWrite-tool gate: "none" registers no write tools; "drafts" registers draft-level writes only (save/delete drafts, upload attachments); "all" registers everything (default). Unrecognized values fail closed to "none".
OFW_CALENDAR_WRITESSet to "true" to register calendar write tools (create/update/delete event) in "drafts" write mode. Events have no draft stage but are reversible. Never overrides "none".
OFW_ALLOW_MARK_READDefault "true". Set "false" to forbid any tool from marking a message read on OurFamilyWizard - reading a body for the first time stamps a co-parent-visible "First Viewed" time that cannot be undone. A ceiling: no per-call argument can raise it.
OFW_FETCH_UNREAD_BODIESDefault "false". Whether ofw_sync_messages fetches unread inbox bodies by default (each fetch stamps a First Viewed time). Capped by OFW_ALLOW_MARK_READ.
OFW_READ_DAYSAllow list of days message content may be read, e.g. "mon,thu" (weekdays, YYYY-MM-DD, or YYYY-MM-DD..YYYY-MM-DD, in DISPLAY_TZ). Other days: subjects only, nothing marked read. Unset = every day.
OFW_NO_READ_DAYSDeny list of days message content may NOT be read (same format as OFW_READ_DAYS; wins over it). Unset = none.
OFW_SESSION_CACHESet to false to disable the on-disk session cache and re-authenticate on every process start. Defaults to enabled.
OFW_SESSION_FILEAbsolute path for the session cache file. Defaults to $MCP_DATA_DIR/.ofw-mcp/session.json.
OFW_EXPENSE_ONLYSet true to register only healthcheck plus expense tools. All profile/dashboard, message, calendar, and journal tools are structurally absent.
OFW_EXPENSE_UPLOAD_ONLYSet true for strict reimbursement upload mode: only healthcheck, ofw_upload_expense_pdf, ofw_create_expense, and ofw_update_expense register. Excludes ofw_mark_expense_reimbursed. Implies OFW_EXPENSE_ONLY.
OFW_INLINE_ATTACHMENTSSet "true" to return attachments as MCP content blocks by default (images render directly; other files come back as embedded resources) instead of writing to disk. Callers can still override per call via the tool's `inline` argument.
OFW_ATTACHMENTS_DIRWhere ofw_download_attachment writes (default ~/Downloads/ofw-mcp). Every disk write is confined to it: a saveTo outside it (after resolving symlinks) is refused, and an existing file is replaced only with force:true.
OFW_UPLOAD_DIRThe only directory ofw_upload_attachment may read from (default: OFW_ATTACHMENTS_DIR). Paths outside it after resolving symlinks, hidden files/dirs, and files over 25 MiB are refused.
OFW_CACHE_IDENTITYExplicit cache-key label; overrides OFW_USERNAME for fetchproxy-only multi-account setups. If you have multiple OFW accounts and want them to use separate caches, set it to a label per profile.
OFW_CACHE_DIROverrides the message cache directory (default ~/.cache/ofw-mcp).
OFW_DISABLE_FETCHPROXYSkip the fetchproxy fallback, so missing credentials become a hard error (useful in headless CI).
OFW_AUTO_REFRESHWhen a cached read comes back EMPTY from a non-fresh cache, the read tools sync the backing folders and answer from the refreshed cache instead of refusing with UNVERIFIED_EMPTY (default off, i.e. refuse). Per-call autoRefresh overrides it. A refresh that does not make the read verifiable still refuses.
OFW_FRESHNESS_TTL_SECONDSHow long a verified-against-OFW folder stays labelled `fresh` in read tools' `freshness` block (default 300). Unusable values fall back to the default — a typo must never widen the window in which stale data reads as current.
OFW_SYNC_MAX_REQUESTSPer-call OFW-request budget for ofw_sync_messages: a positive integer caps how many OFW requests one sync call may make before pausing; the next call resumes where it left off. Unset means unbounded (local installs walk fully in one call).
OFW_REQUEST_TIMEOUT_MSPer-request timeout for OFW API calls, in milliseconds (default 30000). Each retry gets its own fresh window.
OFW_DEBUG_LOGLog every OFW request/response to stderr (Authorization redacted). Diagnostic only.
DISPLAY_TZIANA zone (e.g. America/New_York, the default) for every `<field>Display` value, and the zone a NAIVE source timestamp is assumed to be wall-clock in. OFW's API reports naive local times in the account's own zone, so this must match it. Unrecognized values fall back to the default.
FETCHPROXY_WS_PORTLocalhost port of the fetchproxy concentrator the ContextMint Bridge extension connects to (default 37149). Every MCP on one machine shares it, so move it for all of them or not at all.
FETCHPROXY_WS_HOSTBind interface for the fetchproxy concentrator, a literal IP only (default 127.0.0.1). Keep on loopback; it exists for hosted deployments that run each MCP in its own network namespace.
FETCHPROXY_IDENTITY_DIRAbsolute path where the fetchproxy long-term identity keypair is stored (default ~/.fetchproxy/identity/).

本地服务需要在你的电脑上安装运行环境(如 Node.js 18+)。各客户端的配置文件位置不同,请参考客户端文档。

安装包
npmofw-mcp2.23.0

工具

28

收录的工具列表 · 更新于 今天

此工具无需参数。

详细介绍

OurFamilyWizard MCP

CI npm license

一个 Model Context Protocol 服务,可将 Claude 连接到 OurFamilyWizard,让你能以自然语言访问共同养育消息、日历、费用和日志。

警告

由 AI 开发的项目。 此代码库完全由 Claude Sonnet 4.6 构建并积极维护。没有人审查过其实现。使用前请检查所有代码和工具权限。

你可以做什么

可以向 Claude 提出以下问题:

  • "显示我最近的 OFW 消息"
  • "孩子们下周的日历上有什么?"
  • "列出最近的费用,并告诉我欠了多少"
  • "为今天接孩子的事添加一条日志"
  • "起草一条回复,回应共同养育者发来的上一条消息"

要求

  • Claude Desktop
  • Node.js 22.5 或更高版本(缓存后端使用 node:sqlite)
  • 一个有效的 OurFamilyWizard 账户

使用条款确认

使用此 MCP 服务,即表示你确认并同意以下内容:

1. 此服务会访问你自己的 OurFamilyWizard 账户。 身份验证使用你自己的凭据。它不会,也无法访问共同养育者的账户、孩子的账户或其他任何人的账户。

2. OurFamilyWizard 的条款适用于你对本服务的使用,正如这些条款也适用于你直接使用 OFW 一样。条款中没有明确的反抓取规定;相关条款的范围更广:

用户不得通过并非有意提供的任何方式获取或试图获取任何材料或信息。

关于凭据:“你须全权负责 (1) 严格保管分配给你的身份验证方式,以及 (2) 告知任何获准使用所分配身份验证方式的人(“授权用户”),不得允许他人使用该身份验证方式。” OFW 确实考虑了“授权用户”和由第三方启用的集成,但账户持有人仍须承担责任。

每次调用此服务中的工具,即表示你同意这些条款——维护者于 2026-05-23 阅读了这些条款。

3. 仅供个人及家庭使用。 本项目与 OurFamilyWizard, LLC 或其母公司没有任何关联,也未获得其认可、赞助或与其合作。这是供指定账户持有人使用的个人自动化工具。未经共同养育者同意,不得代表其使用;不得与任何人共享凭据;不得用它批量提取其他家庭的数据。

4. OFW 是法庭记录平台。 OFW 上的消息、费用、日历条目和日志条目可能会被提交至法律诉讼,包括监护权、离婚和育儿计划修改案件。此服务写入 OFW 的任何内容(你保存的草稿、创建的事件、记录的费用),都具有与你亲自输入相同的法律效力。未经阅读并批准,不要让此 MCP 发送消息、创建事件或记录费用。 确认前请审查每一项写入操作。

5. 你须对任何后果承担全部责任——包括技术后果(账户警告、暂停使用)和法律后果(OFW 记录的任何账户活动)。MCP 作者不是你的律师;如果你在正在进行的法律事务中使用 OFW,请先咨询你的律师,再进行任何自动化操作。

本节是维护者基于善意对条款所作的概述,并非法律建议,也不会修改或取代 OurFamilyWizard 的实际服务条款。

安装

1. 克隆并构建
git clone https://github.com/chrischall/ofw-mcp.git
cd ofw-mcp
npm install
npm run build
2. 添加到 Claude Desktop

编辑 Claude Desktop 配置文件:

  • Mac: ~/Library/Application Support/Claude/claude_desktop_config.json
  • Windows: %APPDATA%\Claude\claude_desktop_config.json

在 "mcpServers" 中添加 ofw 条目(如果该键不存在,请创建):

{
  "mcpServers": {
    "ofw": {
      "command": "node",
      "args": ["/absolute/path/to/ofw-mcp/dist/index.js"],
      "env": {
        "OFW_USERNAME": "[email protected]",
        "OFW_PASSWORD": "your-ofw-password"
      }
    }
  }
}

将 /absolute/path/to/ofw-mcp 替换为克隆仓库的实际路径。在 Mac 上,在克隆目录中运行 pwd 即可获取路径。

3. 重启 Claude Desktop

彻底退出(Mac 上按 Cmd+Q,不要只关闭窗口),然后重新启动。

4. 验证

询问 Claude:"我的 OFW 仪表板是什么样的?" — 它应显示未读消息数、即将到来的事件和未结费用。

身份验证

ofw-mcp 会按顺序尝试三种身份验证方式;最先成功的方式将被采用。现有配置无需更改即可继续使用。

  1. 环境变量凭据(旧方式,推荐用于 Claude Desktop)。 设置 OFW_USERNAME + OFW_PASSWORD,服务器便会通过 OFW 的表单端点登录。Claude Desktop 配置示例中展示的就是这种方式。
  2. fetchproxy 备用方式(无需环境变量)。 如果未提供凭据,服务器会在启动时,通过 ContextMint Bridge 浏览器扩展(基于 fetchproxy 构建),从你已经登录的 ourfamilywizard.com 标签页读取一次 localStorage["auth"]。读取一次后,所有 OFW API 调用都会直接从 Node 发出 — 扩展不会处于请求热路径中。从发布页面安装 ContextMint Bridge(Chrome:下载 Chrome zip 文件、解压,然后在开启开发者模式的 chrome://extensions 页面加载解压后的扩展;Safari 暂不可用 — 它将随 ContextMint 应用一同发布,但该应用尚未公开下载 — 因此目前请使用 Chrome),登录 OurFamilyWizard 一次,MCP 即可正常使用。ContextMint Bridge 是 fetchproxy 浏览器扩展的新名称,由同一位维护者开发 — fetchproxy 自身的 README(https://github.com/chrischall/fetchproxy#extension)指向该扩展。其源代码公开于 https://github.com/nullnet-app/contextmint-bridge:你可以自行构建,或使用同目录发布的 .sha256 文件校验发布 zip 文件(shasum -a 256 -c contextmint-bridge-chrome-<version>.zip.sha256)。如果你有多个 OFW 账户,并希望它们使用独立缓存,请为每个配置文件设置不同的 OFW_CACHE_IDENTITY 标签。
  3. 错误。 如果两种方式都不可用,服务器会明确告知你应采取哪种修复措施。设置 OFW_DISABLE_FETCHPROXY=1 可完全跳过 fetchproxy 备用方式(缺少凭据时会直接报错 — 适用于无头 CI)。
凭据选项(环境变量方式)

选项 A — Claude Desktop 配置中的 env 块(如上所示,推荐):

"env": {
  "OFW_USERNAME": "[email protected]",
  "OFW_PASSWORD": "your-ofw-password"
}

选项 B — 项目目录中的 .env 文件:

cp .env.example .env
# edit .env and fill in your credentials

环境变量的优先级始终高于 .env 文件。你也可以直接在命令行中传入它们:

[email protected] OFW_PASSWORD=yourpass node dist/index.js

可用工具

只读工具会自动运行。会发送给共同育儿人或写入法院可见记录的写入操作——下文的 Confirm (server)——由服务器在发送前自行确认(参见服务器端确认);其他写入操作则依赖 MCP 主机自身的审批提示(Confirm)。写入模式列显示工具至少需要哪种 OFW_WRITE_MODE 才能启用——详见下文的写入保护。

工具功能权限写入模式
ofw_get_profile你的个人资料和共同育儿人信息自动任意
ofw_get_notifications控制面板计数(未读消息、即将发生的事件、未结费用)。会更新共同育儿人可见的最后查看状态,因此不标记为只读确认任意
ofw_list_message_folders带未读计数的文件夹——列出消息前先在此获取文件夹 ID自动任意
ofw_list_messages文件夹中的消息自动任意
ofw_get_message单条消息的完整内容自动任意
ofw_sync_messages将消息同步到本地缓存(为避免已读回执,未读消息正文不会被提取)自动任意
ofw_get_unread_sent收件人尚未阅读的已发送消息(来自本地缓存)自动任意
ofw_check_freshness以低成本实时检查缓存是否仍与 OFW 一致——针对每个 ID 检查其仍是 draft,还是已 sent/deleted,无需完整同步自动任意
ofw_status一次实时调用,查看“一切进展如何?”——完整且已验证的草稿清单,以及任意 ID 或草稿键的当前状态自动任意
ofw_download_attachment将消息附件下载到磁盘,或以内嵌提取内容 / 字节的形式提供自动任意
ofw_send_message发送消息确认(服务器)all
ofw_list_drafts草稿消息自动任意
ofw_save_draft创建或更新草稿确认drafts
ofw_delete_draft删除草稿确认drafts
ofw_upload_attachment将上传目录(OFW_UPLOAD_DIR,默认 ~/Downloads/ofw-mcp)中的本地文件上传到 My Files;返回可通过 ofw_send_message/ofw_save_draft 附加的 fileId。共享(shareClass: SHARED)需要模式 all自动(PRIVATE)/确认(服务器)(SHARED)drafts
ofw_list_events指定日期范围内的日历事件自动任意
ofw_create_event创建日历事件确认(服务器),私人事件除外all(或 drafts + OFW_CALENDAR_WRITES)
ofw_update_event更新日历事件若为共享事件,则需确认(服务器)all(或 drafts + OFW_CALENDAR_WRITES)
ofw_delete_event删除日历事件若为共享事件,则需确认(服务器)all(或 drafts + OFW_CALENDAR_WRITES)
ofw_get_expense_totals费用汇总自动任意
ofw_list_expenses费用记录自动任意
ofw_list_expense_categories费用类别 ID 和分摊元数据自动任意
ofw_upload_expense_pdf为某笔费用将收据 PDF 上传到 My Files(SHARED——共同育儿人会立即在 My Files 中看到它,即使费用是私人的)确认(服务器)all
ofw_create_expense登记新费用;支持私人条目和一份收据 PDF确认(服务器)all
ofw_mark_expense_reimbursed不可撤销地记录全部剩余的外部报销;不涉及转账或部分付款;仅上传模式下不可用确认(服务器)all
ofw_update_expense修改费用,例如公开一笔私人费用。只传入要更改的字段;此工具会先读取费用,因此其余内容会保留确认(服务器)all
ofw_list_journal_entries日志条目自动任意
ofw_create_journal_entry创建日志条目确认all
数据新鲜度(OFW_FRESHNESS_TTL_SECONDS)

邮件和草稿读取来自本地缓存,因此结果看起来可能很可靠,实际却已过时几分钟甚至几个月。缓存也无法自行检测某些更改:在 OFW Web 应用中编辑草稿不会更新时间戳,所以除非服务端说明具体情况,否则“没有变化”和“我们没有检查”无法区分。

因此,每个读取工具(ofw_list_messages、ofw_list_drafts、ofw_get_message、ofw_list_message_folders、ofw_sync_messages)都会在数据旁返回一个 freshness 区块:

"freshness": {
  "source": "cache",
  "asOf": "2026-07-20T12:40:00.000Z",
  "ageSeconds": 5231,
  "staleness": "unverified",
  "lastServerSyncAt": "2026-07-20T13:59:00.000Z",
  "syncComplete": false,
  "historyComplete": true,
  "warning": "Served from cache last verified 87 min ago; the last sync did not finish checking drafts. Re-read before asserting current state — call ofw_check_freshness for a cheap live confirmation, or ofw_sync_messages to refresh."
}

只有在本次调用中实时获取数据,或通过实际访问了相应文件夹的同步,在阈值范围内与 OFW 核实过数据时,staleness 才为 fresh。数据超出时限或同步跳过了该文件夹时,状态会降为 unverified;如果从未检查过该文件夹,则为 stale。凡不是 fresh 的状态,都会附带一条易于理解的 warning,说明数据的时效和原因。这种倾向是有意且单向的:误报为 unverified 只会多一次调用,而误报为 fresh 则会让人把记忆中的状态当作当前事实来描述。

草稿还会附带每项的 cacheStatus、asOf 和 serverConfirmed——只有完成草稿遍历并在阈值范围内核实后,该值才为 true。serverConfirmed: false 表示该草稿的存在及未发送状态只是记忆中的信息,并非已知事实;在将其作为当前事实陈述之前,应先调用 ofw_check_freshness。

ofw_check_freshness 是重新核实的轻量方式:一次请求用于比较文件夹计数,每个邮件 ID 各一次请求;不获取正文,也不进行完整同步。基于上述原因,草稿 ID 会按内容修订版本而非时间戳进行比较。对于缓存中标记为草稿、已发送邮件或已读收件箱邮件的 ID,该工具可以自由探测,因为这些操作不会造成任何更改;其他任何 ID 的探测都会将 OurFamilyWizard 中的未读收件箱邮件标记为已读——这是对法院可见记录的不可逆更改——因此除非传入 allowMarkRead: true,否则会跳过这些 ID。

设置 OFW_FRESHNESS_TTL_SECONDS 可调整阈值(默认值为 300,即 5 分钟)。不可用的值会回退到默认值,而不会扩大时限。

它还是我以为的那个状态吗?(ofw_status)

新鲜度回答的是数据有多旧。它无法回答这个实体是否仍处于我以为的状态——这是另一种故障。已发送的草稿仍然存在于服务端,因此“此 ID 是否存在?”会返回 true,恰恰是在答案最重要的情况下。

任何状态摘要都应以 ofw_status 调用结果为依据:

{
  "checkedAt": "2026-07-28T09:12:00.000Z",
  "requested": [
    { "id": 538279699, "state": "sent", "sentAt": "2026-07-27T23:31:09", "inSync": false },
    { "id": 538086428, "state": "draft", "inSync": true }
  ],
  "complete": true
}
  • state 为 draft、sent、received、deleted 或 unknown,数据实时读取自 OFW。unknown 表示问题尚未得到回答;它并不等同于“一切正常”。
  • 不带参数时,它会返回完整的草稿清单,并先与 OFW 核实。
  • complete: true 表示快照的每一部分都已实时确认。如果该值为 false,incompleteReasons 会说明哪些部分尚未确认,此时不能以该负载为依据陈述数量。

草稿键。 每次编辑草稿都会生成新的 OFW ID(ofw_save_draft 采用先创建再删除的方式进行替换,因为 OFW 的原地更新端点会静默地不执行任何操作)。因此,ofw_save_draft 还会返回一个 draftKey,它在每次编辑后都保持不变,并且会随邮件进入“已发送”状态。ofw_status(draftKeys: ["dk_…"]) 会将其解析为当前 ID 和状态——包括 state: "sent" 及 sentMessageId——因此,“我正在处理的草稿发生了什么?”只需一次调用,无须猜测哪个 ID 才是当前 ID。

永远不会根据过期缓存报告不存在

缓存读取结果为空时,其形式与已验证的「不存在」完全相同。因此,当结果为空且底层缓存不是 fresh 时,ofw_list_messages、ofw_list_drafts 和 ofw_get_unread_sent 会拒绝回答:

{
  "result": "UNVERIFIED_EMPTY",
  "reason": "No drafts were found, but the backing cache is \"unverified\" — it was last verified 207 min ago. Refusing to report absence from unverified data…",
  "remedy": "Call ofw_sync_messages(folders:[\"drafts\"]) and retry, re-call with autoRefresh:true, or use ofw_status(includeDraftInventory:true) for a single live answer.",
  "complete": false
}

误报不存在(「不,它从未发送过」)比拒绝回答更危险,因为这听起来像是确定的答案。非空结果绝不会被隐去——过期缓存中找到的内容仍然是存在的证据,并会像之前一样标注其缓存时长。传入 autoRefresh: true(或设置 OFW_AUTO_REFRESH=true),即可让工具同步后作答,而不是拒绝;如果刷新后仍无法验证读取结果,工具仍会拒绝回答。

每次列表读取还会附带一个明确的 complete 布尔值,用来说明结果集是否完整——即「截至 asOf,这是 OurFamilyWizard 上所有匹配项」;如果结果不完整,completeNote 会说明缺少哪些内容。在说明数量之前,请先检查该字段。

记录外部报销

ofw_mark_expense_reimbursed 用于记录已在 OFW 之外报销的款项。它绝不会转账、调用 /payments 或发起银行操作。OFW 警告,标记为已报销后无法撤销。只有在 OFW_WRITE_MODE=all 时才会注册此工具,包括仅费用模式;在 OFW_EXPENSE_UPLOAD_ONLY=true 时则绝不会注册,受限工具列表保持不变。

传入费用 ID 和以货币单位表示的 expectedRemainingAmount,金额必须为正数且精确到分。例如,合成费用总额为 96.99,其中 totalOwed=48.50 且 leftToPay=48.50,则需要传入 { "expenseId": 81001, "expectedRemainingAmount": 48.50 }——不能传入 96.99 或部分金额。首次调用会预览标题、作者/付款方用户 ID、分摊/类别、子女、隐私设置、总额、应付金额、剩余余额和当前状态。请核实相关方,并确认外部报销确已完成,然后明确批准预览,再用相同参数返回其 confirmToken(或批准受支持客户端的确认提示)。

仅支持状态为 OPEN、APPROVED 或 PARTIAL,且 subStatus=NONE、余额为正的记录。详情缺失或格式错误、状态未知或待处理、余额不一致、ID 不匹配或金额不匹配时,都会拒绝操作且不会写入。对于已处于 PAID 状态且剩余余额为零的记录,无论付款方式如何,都会返回 ALREADY_PAID,且不会写入;这不会将电子付款重新标记。

确认操作会绑定整条详情记录。写入前会立即重新 GET 最新详情,任何变化(包括收据或元数据编辑)都会导致拒绝。唯一的变更操作是使用 { "status": "MARKED_REIMBURSED" } 执行 PUT /pub/v2/expense/expenses/{id}/status。随后必须再次 GET 详情,并确认状态为 PAID、subStatus=NONE、剩余余额为零、paymentType="Marked reimbursed",且预期的状态/余额变更之外的字段均未改变,之后才会报告成功。已知的派生余额显示字段可能正常变化;购买总额、应付总额等数值以及其他受保护字段必须保持不变。我们曾使用一条经用户授权的记录,手动验证并接受了公开 Web 应用的状态请求契约及其导致的状态转换。自动化测试仍仅使用模拟数据,包含的也是合成记录。若后端出现受支持转换之外的意外变化,系统仍会谨慎地判定失败,而不会声称验证成功。

这些只是尽力满足的前置条件,并非原子条件更新:OFW 在此处不提供条件状态写入契约,因此无法阻止最后一次 GET 之后发生并发编辑。若变更失败且结果不明确(超时/网络错误/5xx),或回读失败/不匹配,则返回 EXPENSE_REIMBURSEMENT_UNCONFIRMED,并设置 mayHaveLanded=true。**不要自动重试或回滚。**请在 ourfamilywizard.com 的 Expenses 日志中检查该费用,并核实其状态后再尝试。OFW 明确拒绝的请求仍会作为错误返回。自动化测试不会使用真实账户或私人家庭/财务数据。

服务端确认(MCP_CONFIRM_MODE)

上文标记为确认(服务端)的写入操作——发送消息、记录费用、创建/更新/删除共享日历事件,以及以 SHARED 上传文件——由此服务确认,而不是交由宿主处理。能够显示确认提示词的客户端(Claude Code)会显示确认提示词,并预览将要执行的确切操作,除非 MCP_CONFIRM_ELICITATION=off。无法显示确认提示词的客户端(claude.ai、Claude Desktop)则分两步处理:首次调用不会写入任何内容,而是返回预览和一个 confirmToken;只有使用相同参数和该令牌再次调用,才会继续执行。

预览会列出你要批准的内容——收件人姓名、主题和完整正文、回复对象、附件文件名;费用金额和说明;事件的标题、日期、时间、可见性,以及(更新时)更改前后的内容。令牌与这些内容精确绑定:正文、金额或收件人不同都会被拒绝;如果预览后 OurFamilyWizard 中的草稿或事件发生了变化(例如,共同家长编辑了事件),即使设置了 force: true 也会被拒绝。令牌只能使用一次,且会过期。

变量默认值
MCP_CONFIRM_MODEask-user对于无法显示提示词的客户端,受控写入会如何处理。ask-user:分两步进行,模型必须先在聊天中获得你的批准,才能使用令牌。auto:分两步进行,但模型在自行检查预览后即可使用令牌。refuse:拒绝此类写入(请在 ourfamilywizard.com 上执行)。无法识别的值会按 refuse 处理。
MCP_CONFIRM_ELICITATIONonoff 表示永不显示确认提示词,因此所有客户端都会采用 MCP_CONFIRM_MODE 指定的处理方式。对于声称支持提示词但从不显示提示词的客户端,请设置此项(受控写入会挂起——opencode 2.0.x)。其他任何值都会保持为 on,并在 stderr 上显示警告。
MCP_CONFIRM_TTL_SECONDS600令牌的有效时长。
MCP_CONFIRM_SECRET每个进程随机生成签名密钥;只有在令牌必须在服务重启后继续有效时才设置。在 mcp-host 上,宿主会提供稳定的子进程专用密钥(MCP_HOST_CONFIRM_SECRET),已使用的令牌会记录在 MCP_DATA_DIR 下,因此即使空闲重启,批准状态也会保留。

私有事件和 PRIVATE 上传不受此门控,因为共同家长看不到这些内容。下方的 OFW_WRITE_MODE 仍是其底层的结构性保护层:写入模式排除的工具根本不会存在。

写入超时不代表写入失败。 如果 ofw_send_message、ofw_create_expense、ofw_create_event 或 ofw_create_journal_entry 发生连接中断或超时,且 OFW 未给出明确答复,结果将为 SEND_UNCONFIRMED / EXPENSE_UNCONFIRMED / EVENT_UNCONFIRMED / JOURNAL_UNCONFIRMED:操作可能已经成功。重试前请检查对应的列表工具(或 ourfamilywizard.com),否则共同家长可能会看到重复内容。

写入保护(OFW_WRITE_MODE)

上文提到的宿主“确认”权限只是对 MCP 宿主的一个建议——如果宿主配置为自动批准工具(或用户曾点击过一次“始终允许”),模型输出和消息发送之间就没有任何阻隔。由于 OurFamilyWizard 是法庭记录平台,服务还支持结构性门控:在服务的 env 块中设置 OFW_WRITE_MODE,所选级别以上的工具将永不注册,因此无论宿主设置如何,也无论提示词注入了什么指令,都无法调用这些工具。

OFW_WRITE_MODE可用内容
none仅可读取/同步/搜索。不存在写入工具。
drafts增加草稿级写入:ofw_save_draft、ofw_delete_draft、ofw_upload_attachment(仅限 PRIVATE)。任何内容都不会写入法庭可见记录——由 AI 准备,只有登录 OFW Web 界面的人工用户才能发送。
all全部功能(默认值——完全向后兼容)。

无法识别的值会安全地回退为 none,并在 stderr 上显示警告——拼写错误绝不会在无提示的情况下授予写入权限。

仅报销部署

对于专用的报销集成,你还可以从结构上移除无关的 OFW 功能:

设置已注册的接口
OFW_EXPENSE_ONLY=true仅包含 ofw_healthcheck 和报销工具。个人资料/仪表板、消息、日历和日志工具均不存在。
OFW_EXPENSE_UPLOAD_ONLY=true最严格模式:仅包含 ofw_healthcheck、ofw_upload_expense_pdf、ofw_create_expense 和 ofw_update_expense。报销总额和列表功能也会移除。此标志隐含启用 OFW_EXPENSE_ONLY。

这些标志是在注册时生效的限制,而不是提示词指令。主机无法调用从未注册的工具。无法识别的非空标志值会安全地限制为受限状态。底层仍适用 OFW_WRITE_MODE;对于严格的上传工作流,请设置 OFW_WRITE_MODE=all,以启用报销创建功能。

推荐的仅报销部署:

OFW_EXPENSE_UPLOAD_ONLY=true
OFW_WRITE_MODE=all
OFW_ALLOW_MARK_READ=false
读取也是一种写入(OFW_ALLOW_MARK_READ)

首次获取消息正文时,会在 OurFamilyWizard 上将其标记为已读,并添加共同养育人可见的“首次查看”时间戳。这是记录的一部分,无法撤销——而且它是普通读取操作的副作用,因此不受 OFW_WRITE_MODE 管理。

默认情况下不会有任何变化:读取行为与以往完全相同。如果需要,可使用以下两种控制方式:

设置效果
ofw_get_message(allowMarkRead: false)如果获取操作会为收件箱中的未读消息添加时间戳,则拒绝该操作,并返回结构化的 MARK_READ_BLOCKED 载荷。缓存的正文、已发送的消息和已读消息仍会正常返回——这些消息都不会触发标记。
OFW_ALLOW_MARK_READ=false部署范围的上限。任何工具都不能添加时间戳:ofw_get_message 会拒绝操作,ofw_check_freshness/ofw_status 会忽略 allowMarkRead:true,ofw_sync_messages 会忽略 fetchUnreadBodies:true。单次调用参数无法解除此限制。

OFW_FETCH_UNREAD_BODIES=true 会使 ofw_sync_messages 默认获取未读消息正文(除非设置,否则默认关闭)——适用于已读回执属于常规操作的场景。此设置受 OFW_ALLOW_MARK_READ 限制。

消息读取日(OFW_READ_DAYS/OFW_NO_READ_DAYS)

如果你只能在特定日期读取 OFW 消息,请列出这些日期。在其他日期,你仍可查看消息已送达的信息——发件人、日期、主题——但消息正文和附件会被隐藏,也不会在 OurFamilyWizard 上标记为已读。

OFW_READ_DAYS=mon,thu                       # allow list: read only on these days
OFW_NO_READ_DAYS=2026-12-24..2026-12-26     # deny list: never on these (wins over the allow list)

有效值可以是星期名称(mon、Monday,等等)、日期(2026-12-25)或包含首尾日期的范围(2026-12-24..2026-12-31),以逗号或空格分隔;“今天”按 DISPLAY_TZ 确定。未设置时,每天都可读取。无法解析的值会安全地导致每天都隐藏内容,并在 stderr 上发出警告。与 OFW_ALLOW_MARK_READ 一样,单次调用参数无法覆盖此设置。拒绝操作时会返回 READING_BLOCKED 和 nextReadableDay。在这些日期,ofw_sync_messages 不会下载任何消息内容(只获取列表:发件人、日期、主题);在可读取日进行的首次同步会补全正文,但收件箱中的未读消息仍需等你打开后才会获取。草稿仍可读取,发送操作不受影响。

日历选择性启用(OFW_CALENDAR_WRITES)

日历事件介于两种消息权限之间:它们没有草稿阶段(创建的事件会立即显示在共享记录中),但与已发送的消息不同,事件可以撤销——之后可以编辑或删除。如果你使用 drafts 模式,但允许直接写入日历,可设置 OFW_CALENDAR_WRITES=true,以额外注册 ofw_create_event、ofw_update_event 和 ofw_delete_event。此标志在 all 模式下是冗余的,且永远不会覆盖 none。

出站流量允许列表

每个携带 OFW 令牌或登录凭据的请求,都会在调用 fetch 前检查其构造的 URL 所属主机——服务器拒绝将这些请求发送到 ofw.ourfamilywizard.com 以外的任何主机。目前检查总是通过,这正是目的所在:它让“凭据只会发送给 OFW”成为结构性不变量,而非依赖代码审查的承诺。因此,后续重构或受入侵的依赖若将请求指向其他位置,就会触发异常,而不会把你的持有者令牌或消息发送到外部主机。这是对我们构造的 URL 进行的预检,并不是会跟随重定向的出站流量过滤器。

服务器还会联系另一个主机 *.oaiusercontent.com,但仅用于 ofw_upload_expense_pdf 的 url 模式:它会从该签名文件 URL 下载收据 PDF,不附带 OFW 凭据,并在每次重定向时重新检查目标是否匹配相同后缀,同时限制文件大小并要求文件确实为 PDF。托管注册的出站流量允许列表(mint.yaml)恰好列出这两个主机。

故障排查

“0 条消息”——Claude 可能读取了通知数量,而非实际消息。请明确要求:“列出我 OFW 收件箱中的消息”,或 “先使用 ofw_list_message_folders,再使用 ofw_list_messages”。

“OFW auth: set OFW_USERNAME + OFW_PASSWORD, or install ContextMint Bridge…”——两种身份验证方式都未配置。请在 Claude Desktop 配置中的 env 块填写信息,或安装 ContextMint Bridge,并在浏览器中登录 ourfamilywizard.com。

“fetchproxy fallback failed” / “ContextMint Bridge is down”——未配置环境变量方式,且无法连接该扩展。请确认已安装 ContextMint Bridge、已登录 OFW,并且扩展正在运行(打开扩展弹出窗口)。如果要完全禁用回退方式,请设置 OFW_DISABLE_FETCHPROXY=1。

“OFW login not attempted — this OurFamilyWizard email and password were already rejected”——OFW 在本次会话中此前已拒绝这些凭据。OFW 会将登录失败次数计入账户,因此服务器不会在每次调用时都重新发送已被拒绝的密码。请将 OFW_USERNAME / OFW_PASSWORD 更新为当前登录凭据(或重启服务器),然后重试。

403 Forbidden——凭据错误。请在 ofw.ourfamilywizard.com 验证用户名和密码。

Claude 中未显示工具——前往 Claude Desktop → Settings → Developer 查看已连接的服务器和任何错误输出。编辑配置后,请确保完全退出并重新启动应用。

在 Mac 上找不到配置文件——在 Finder 中按 Cmd+Shift+G,然后粘贴 ~/Library/Application Support/Claude/。

安全性

  • 凭据仅保存在本地配置文件或 .env 中
  • 凭据作为环境变量传递给服务器,绝不会写入日志
  • 服务器使用与 Web 应用相同的登录流程通过 OFW 进行身份验证
  • 请使用强度高且独一无二的 OFW 密码
  • 携带 OFW 凭据的请求仅允许发送到 ofw.ourfamilywizard.com;唯一联系的其他主机是用于 ofw_upload_expense_pdf 的 url 模式的收据下载主机(参见出站流量允许列表)
本地数据及其删除方法

服务会在此计算机上存储共同养育数据,且只有你的用户帐户可以读取:

内容位置权限
消息缓存(消息正文、收件人、草稿、附件记录)~/.cache/ofw-mcp/<hash>.db(以及 -wal/-shm),或 OFW_CACHE_DIR目录为 0700,文件为 0600
下载的附件~/Downloads/ofw-mcp/,或 OFW_ATTACHMENTS_DIR服务创建的目录为 0700,文件为 0600
会话令牌$MCP_DATA_DIR/.ofw-mcp/session.json,或 OFW_SESSION_FILE0600

缓存会保留所有已同步的数据,直到你将其删除;数据不会自动过期。当你不再需要这些数据时——例如帐户已关闭、案件已结束,或你准备转让这台计算机——请退出 MCP 宿主并删除数据:

rm -rf ~/.cache/ofw-mcp        # or your OFW_CACHE_DIR
rm -rf ~/Downloads/ofw-mcp     # or your OFW_ATTACHMENTS_DIR
rm -f  ~/.ofw-mcp/session.json # or your OFW_SESSION_FILE

下次启动时,系统会通过 ofw_sync_messages 从 OurFamilyWizard 重建缓存。你自行指定的 OFW_ATTACHMENTS_DIR 目录会保留其原有权限(它可能是有意共享的);只有服务创建的目录和默认目录 ~/Downloads/ofw-mcp 会受到限制。

对于托管连接器(mcp-host),相同的数据会存储在注册项的持久化数据目录中,而非你的计算机上:消息缓存位于 $HOME/.cache/ofw-mcp/,会话令牌位于 $MCP_DATA_DIR/.ofw-mcp/session.json,浏览器桥接身份位于 $HOME/.fetchproxy/identity/,已使用的确认令牌随机数位于 $MCP_DATA_DIR/.mcp-confirm/。下载内容会以内联形式返回,绝不会写入磁盘。上述命令无法清除这些数据——该目录的保留时长,以及断开连接或删除注册项是否会将其移除,取决于宿主的保留政策,而非此服务。

开发

npm test         # tsc typecheck, then the vitest suite
npm run build    # tsc → dist/, then esbuild bundle → dist/bundle.js
npm run dev      # node --env-file=.env dist/index.js (requires built dist)

主分支受保护。所有更改都通过 PR 合并——使用 gh pr create --label <release-notes-label> 创建 PR,并在你对自动审核反馈满意后添加 ready-to-merge。完整的 PR 和发布流程请参阅 CLAUDE.md。

项目结构
src/
  index.ts          MCP server entry (McpServer + StdioServerTransport)
  client.ts         OFW HTTP client with Bearer token + 401/429 retry
  auth.ts           resolveAuth(): env-var creds → fetchproxy → error
  auth-password.ts  Spring Security form login (legacy env-var path)
  cache.ts          SQLite cache (messages, drafts, attachments, sync state)
  sync.ts           Folder ID resolution + per-folder sync logic
  config.ts         Cache dir, attachment dir, env parsing
  tools/
    _shared.ts      Recipient mapping, response helpers, path expansion
    user.ts         ofw_get_profile, ofw_get_notifications
    messages.ts     Folders, list, get, send, drafts, sync, attachments
    calendar.ts     List, create, update, delete events
    expenses.ts     Totals, list, create
    journal.ts      List, create entries
tests/              Mirrors src/; mocks OFWClient.request via vi.spyOn
身份验证流程

身份验证解析逻辑位于 src/auth.ts。按优先级依次有三种路径:

  1. 存在环境变量 → src/auth-password.ts 执行旧版 OFW Spring Security 表单登录:
    1. GET /ofw/login.form — 建立会话 Cookie
    2. POST /ofw/login — 提交凭据,返回 { auth: "<token>" }
  2. 不存在环境变量(且未设置 OFW_DISABLE_FETCHPROXY) → @fetchproxy/bootstrap 从用户已登录的 ourfamilywizard.com 标签页中读取一次 localStorage["auth"] 和 localStorage["tokenExpiry"],然后关闭桥接。
  3. 未配置任何内容 → 抛出错误,并明确说明两种修复方法。

两种路径都会向 OFWClient 返回 Bearer 令牌,之后客户端会在 Node 中通过 Authorization: Bearer <token> 发起操作——fetchproxy 不会处于请求热路径中。遇到 401 时,客户端会重新解析身份验证信息并重试一次。令牌会缓存 6h(环境变量路径),或缓存至 tokenExpiry 指定的时间(fetchproxy 路径)。

安装扩展的说明也请参阅 fetchproxy README。

许可证

MIT

评论

支持 Markdown

第一次接触 MCP?

从协议是什么,到在客户端里装上第一个服务,这几条回答覆盖了大多数入门问题。

一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。