ofw-mcp 2.10.2 → 2.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +1 -1
- package/dist/auth.js +86 -56
- package/dist/bundle.js +740 -228
- package/dist/cache/store.js +6 -1
- package/dist/client.js +24 -12
- package/dist/index.js +1 -1
- package/dist/session-cache.js +78 -0
- package/dist/tools/expenses.js +21 -3
- package/dist/tools/journal.js +21 -3
- package/dist/tools/messages.js +60 -6
- package/dist/tools/pagination.js +120 -0
- package/mint.yaml +143 -0
- package/package.json +8 -6
- package/server.json +14 -2
- package/skills/ofw/SKILL.md +5 -4
package/mint.yaml
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
version: 1
|
|
2
|
+
name: OurFamilyWizard
|
|
3
|
+
slug: ofw
|
|
4
|
+
summary: >-
|
|
5
|
+
OurFamilyWizard co-parenting tools for Claude — messages, calendar,
|
|
6
|
+
expenses, and journal
|
|
7
|
+
#
|
|
8
|
+
# Hosting note (a comment, not user-facing summary text): this is a
|
|
9
|
+
# BROWSER-BRIDGE MCP — it reaches its site through the user's signed-in
|
|
10
|
+
# tab via the fetchproxy bridge. A bridged registration also needs runtime
|
|
11
|
+
# `fly-shared`, `bridge: true` and a `bridgePortEnv`, which are registration
|
|
12
|
+
# fields this manifest has no schema for (set them over the control API).
|
|
13
|
+
# `state.dataDir` below is required for `bridge`.
|
|
14
|
+
env:
|
|
15
|
+
- name: OFW_USERNAME
|
|
16
|
+
required: false
|
|
17
|
+
help: >-
|
|
18
|
+
Your OurFamilyWizard login email address. Optional — if omitted, the
|
|
19
|
+
server falls back to the fetchproxy browser extension (requires being
|
|
20
|
+
signed in to ourfamilywizard.com).
|
|
21
|
+
- name: OFW_PASSWORD
|
|
22
|
+
secret: true
|
|
23
|
+
required: false
|
|
24
|
+
help: >-
|
|
25
|
+
Your OurFamilyWizard password. Optional — see OFW_USERNAME.
|
|
26
|
+
- name: OFW_WRITE_MODE
|
|
27
|
+
required: false
|
|
28
|
+
help: >-
|
|
29
|
+
Write-tool gate: "none" registers no write tools; "drafts" registers
|
|
30
|
+
draft-level writes only (save/delete drafts, upload attachments); "all"
|
|
31
|
+
registers everything (default). Unrecognized values fail closed to "none".
|
|
32
|
+
- name: OFW_CALENDAR_WRITES
|
|
33
|
+
required: false
|
|
34
|
+
help: >-
|
|
35
|
+
Set to "true" to register calendar write tools (create/update/delete
|
|
36
|
+
event) in "drafts" write mode. Events have no draft stage but are
|
|
37
|
+
reversible. Never overrides "none".
|
|
38
|
+
- name: OFW_ALLOW_MARK_READ
|
|
39
|
+
required: false
|
|
40
|
+
help: >-
|
|
41
|
+
Default "true". Set "false" to forbid any tool from marking a message read
|
|
42
|
+
on OurFamilyWizard - reading a body for the first time stamps a
|
|
43
|
+
co-parent-visible "First Viewed" time that cannot be undone. A ceiling: no
|
|
44
|
+
per-call argument can raise it.
|
|
45
|
+
- name: OFW_FETCH_UNREAD_BODIES
|
|
46
|
+
required: false
|
|
47
|
+
help: >-
|
|
48
|
+
Default "false". Whether ofw_sync_messages fetches unread inbox bodies by
|
|
49
|
+
default (each fetch stamps a First Viewed time). Capped by
|
|
50
|
+
OFW_ALLOW_MARK_READ.
|
|
51
|
+
- name: OFW_INLINE_ATTACHMENTS
|
|
52
|
+
required: false
|
|
53
|
+
help: >-
|
|
54
|
+
When on, ofw_download_attachment returns bytes inline as MCP content
|
|
55
|
+
(images render directly; other files come back as embedded resources)
|
|
56
|
+
instead of writing to disk. Recommended for sandboxed hosts like Claude
|
|
57
|
+
Desktop where the model cannot read files written to ~/Downloads. Callers
|
|
58
|
+
can still override per-call via the tool's `inline` argument.
|
|
59
|
+
- name: OFW_ATTACHMENTS_DIR
|
|
60
|
+
required: false
|
|
61
|
+
help: >-
|
|
62
|
+
Directory where ofw_download_attachment writes files when not returning
|
|
63
|
+
inline. Defaults to ~/Downloads/ofw-mcp/. Pick a directory that is
|
|
64
|
+
readable by your MCP host.
|
|
65
|
+
- name: DISPLAY_TZ
|
|
66
|
+
required: false
|
|
67
|
+
help: >-
|
|
68
|
+
IANA time zone (e.g. America/New_York) that every rendered *…Display*
|
|
69
|
+
field uses, and the zone a naive OFW timestamp is assumed to be in. Must
|
|
70
|
+
match the OFW account's own zone. Never a fixed offset — that would be an
|
|
71
|
+
hour wrong for half the year.
|
|
72
|
+
- name: OFW_DEBUG_LOG
|
|
73
|
+
required: false
|
|
74
|
+
help: >-
|
|
75
|
+
Set to 1 to write verbose request/response diagnostics to stderr. Off by
|
|
76
|
+
default; useful when a read is failing and you need to see the upstream
|
|
77
|
+
exchange.
|
|
78
|
+
- name: OFW_AUTO_REFRESH
|
|
79
|
+
required: false
|
|
80
|
+
help: >-
|
|
81
|
+
Set to true and read tools sync the backing folders themselves and answer
|
|
82
|
+
from the refreshed cache, instead of refusing a stale read. The refusal
|
|
83
|
+
still fires if the refresh does not make the read verifiable.
|
|
84
|
+
- name: OFW_CACHE_DIR
|
|
85
|
+
required: false
|
|
86
|
+
help: >-
|
|
87
|
+
Directory for persisted session/cache state. Relates to state.dataDir
|
|
88
|
+
below; leave unset to use the default under $HOME.
|
|
89
|
+
- name: OFW_CACHE_IDENTITY
|
|
90
|
+
required: false
|
|
91
|
+
help: >-
|
|
92
|
+
Labels the per-user SQLite cache file. Useful when authenticating via the
|
|
93
|
+
browser bridge, where OFW_USERNAME is not set and the cache would
|
|
94
|
+
otherwise fall back to a shared default name.
|
|
95
|
+
- name: OFW_DISABLE_FETCHPROXY
|
|
96
|
+
required: false
|
|
97
|
+
help: >-
|
|
98
|
+
Set to 1 to disable the browser-bridge fallback, making missing
|
|
99
|
+
credentials a hard error. Recommended for a hosted registration, which has
|
|
100
|
+
no browser to fall back to.
|
|
101
|
+
- name: OFW_FRESHNESS_TTL_SECONDS
|
|
102
|
+
required: false
|
|
103
|
+
help: >-
|
|
104
|
+
Age in seconds past which a read result's freshness block downgrades to
|
|
105
|
+
"unverified" and grows a warning (default 300). Unset means never
|
|
106
|
+
downgrade.
|
|
107
|
+
- name: OFW_REQUEST_TIMEOUT_MS
|
|
108
|
+
required: false
|
|
109
|
+
help: >-
|
|
110
|
+
Per-request timeout in milliseconds. Raise it if calls time out on slow
|
|
111
|
+
upstream responses.
|
|
112
|
+
- name: OFW_SYNC_MAX_REQUESTS
|
|
113
|
+
required: false
|
|
114
|
+
help: >-
|
|
115
|
+
Caps the OFW requests one ofw_sync_messages call may make before pausing;
|
|
116
|
+
the next call resumes where it left off. Set a positive integer when
|
|
117
|
+
hosted, where a per-call request cap can otherwise truncate a deep
|
|
118
|
+
backfill.
|
|
119
|
+
- name: OFW_SESSION_CACHE
|
|
120
|
+
required: false
|
|
121
|
+
help: >-
|
|
122
|
+
Set to false to disable the on-disk session cache and re-authenticate on
|
|
123
|
+
every process start. Defaults to enabled.
|
|
124
|
+
- name: OFW_SESSION_FILE
|
|
125
|
+
required: false
|
|
126
|
+
help: >-
|
|
127
|
+
Absolute path for the session cache file. Defaults to
|
|
128
|
+
$MCP_DATA_DIR/.ofw-mcp/session.json.
|
|
129
|
+
state:
|
|
130
|
+
dataDir: true
|
|
131
|
+
reason: >-
|
|
132
|
+
Two things live here. The fetchproxy identity is at
|
|
133
|
+
$HOME/.fetchproxy/identity/<name>.json and the pair code derives from it —
|
|
134
|
+
without a persistent $HOME every cold start mints a fresh identity and
|
|
135
|
+
re-prompts pairing in the browser, and the API refuses bridge without it.
|
|
136
|
+
The session token is cached at $MCP_DATA_DIR/.ofw-mcp/session.json (0600);
|
|
137
|
+
OFW has no refresh grant, so without it every cold start re-runs the full
|
|
138
|
+
login for a token that was still good for up to six hours.
|
|
139
|
+
egress:
|
|
140
|
+
allow:
|
|
141
|
+
# Only hosts the SERVER process actually fetches. Hosts that appear
|
|
142
|
+
# solely in a URL this server BUILDS and returns are excluded.
|
|
143
|
+
- ofw.ourfamilywizard.com
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ofw-mcp",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.12.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"mcpName": "io.github.chrischall/ofw-mcp",
|
|
6
6
|
"description": "OurFamilyWizard MCP server for Claude — developed and maintained by AI (Claude Code)",
|
|
@@ -21,18 +21,20 @@
|
|
|
21
21
|
".claude-plugin",
|
|
22
22
|
"skills",
|
|
23
23
|
".mcp.json",
|
|
24
|
-
"server.json"
|
|
24
|
+
"server.json",
|
|
25
|
+
"mint.yaml"
|
|
25
26
|
],
|
|
26
27
|
"scripts": {
|
|
27
28
|
"build": "tsc && npm run bundle",
|
|
28
29
|
"bundle": "esbuild src/index.ts --bundle --platform=node --format=esm --external:dotenv --banner:js='import { createRequire as __createRequire } from \"module\"; const require = __createRequire(import.meta.url);' --outfile=dist/bundle.js",
|
|
29
30
|
"dev": "node --env-file=.env dist/index.js",
|
|
30
|
-
"test": "vitest run",
|
|
31
|
-
"test:coverage": "vitest run --coverage",
|
|
32
|
-
"test:watch": "vitest"
|
|
31
|
+
"test": "npm run typecheck && vitest run",
|
|
32
|
+
"test:coverage": "npm run typecheck && vitest run --coverage",
|
|
33
|
+
"test:watch": "vitest",
|
|
34
|
+
"typecheck": "tsc -p tsconfig.json --noEmit"
|
|
33
35
|
},
|
|
34
36
|
"dependencies": {
|
|
35
|
-
"@chrischall/mcp-utils": "^0.
|
|
37
|
+
"@chrischall/mcp-utils": "^0.18.0",
|
|
36
38
|
"@fetchproxy/bootstrap": "^2.0.0",
|
|
37
39
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
38
40
|
"dotenv": "^17.4.2",
|
package/server.json
CHANGED
|
@@ -6,12 +6,12 @@
|
|
|
6
6
|
"url": "https://github.com/chrischall/ofw-mcp",
|
|
7
7
|
"source": "github"
|
|
8
8
|
},
|
|
9
|
-
"version": "2.
|
|
9
|
+
"version": "2.12.0",
|
|
10
10
|
"packages": [
|
|
11
11
|
{
|
|
12
12
|
"registryType": "npm",
|
|
13
13
|
"identifier": "ofw-mcp",
|
|
14
|
-
"version": "2.
|
|
14
|
+
"version": "2.12.0",
|
|
15
15
|
"transport": {
|
|
16
16
|
"type": "stdio"
|
|
17
17
|
},
|
|
@@ -52,6 +52,18 @@
|
|
|
52
52
|
"description": "Default \"false\". Whether ofw_sync_messages fetches unread inbox bodies by default (each fetch stamps a First Viewed time). Capped by OFW_ALLOW_MARK_READ.",
|
|
53
53
|
"isRequired": false,
|
|
54
54
|
"format": "string"
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
"name": "OFW_SESSION_CACHE",
|
|
58
|
+
"description": "Set to false to disable the on-disk session cache and re-authenticate on every process start. Defaults to enabled.",
|
|
59
|
+
"isRequired": false,
|
|
60
|
+
"format": "string"
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
"name": "OFW_SESSION_FILE",
|
|
64
|
+
"description": "Absolute path for the session cache file. Defaults to $MCP_DATA_DIR/.ofw-mcp/session.json.",
|
|
65
|
+
"isRequired": false,
|
|
66
|
+
"format": "string"
|
|
55
67
|
}
|
|
56
68
|
]
|
|
57
69
|
}
|
package/skills/ofw/SKILL.md
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
name: ofw
|
|
2
|
+
name: ofw
|
|
3
3
|
description: This skill should be used when the user asks about OurFamilyWizard (OFW) co-parenting data. Triggers on phrases like "check OFW", "OurFamilyWizard inbox", "OFW messages", "OFW calendar", "OFW expenses", "what did my co-parent say", "log an expense in OFW", "OFW journal", or any request involving co-parenting messages, calendar events, shared expenses, or journal entries.
|
|
4
4
|
---
|
|
5
5
|
|
|
@@ -92,11 +92,11 @@ Always pass `--config ~/.mcporter/mcporter.json` unless a local `config/mcporter
|
|
|
92
92
|
|------|-------|
|
|
93
93
|
| `ofw_sync_messages(folders?, deep?, fetchUnreadBodies?)` | Sync OFW → local cache. **Call first if the cache might be stale.** Returns unread inbox hints (bodies not fetched, to avoid mark-as-read). |
|
|
94
94
|
| `ofw_list_message_folders` | List OFW folders with unread counts. Most reads use the cache; this is mainly for folder IDs and live unread counts. |
|
|
95
|
-
| `ofw_list_messages(folderId?, since?, until?, q?, page?, size?, autoRefresh?)` | Cache-backed list. Supports folder ("inbox"/"sent"/"both"), date range, and substring search. Returns `complete` for the RESULT SET
|
|
95
|
+
| `ofw_list_messages(folderId?, since?, until?, q?, sort?, page?, size?, autoRefresh?)` | Cache-backed list. Supports folder ("inbox"/"sent"/"both"), date range, and substring search. `sort:"oldest"` starts at the old end of a range instead of paging to it (default `"newest"`). Returns `complete` for the RESULT SET plus **`nextPage`** (null when done) — and the paging keys come FIRST in the JSON, before `messages`. `returned` carries the record count as a scalar, beside `total`. An **empty** result from a non-fresh cache is refused (`UNVERIFIED_EMPTY`) — pass `autoRefresh:true` to sync and answer instead. |
|
|
96
96
|
| `ofw_get_message(messageId, allowMarkRead?)` | Read a message OR draft body. Cache-first. Ids in the drafts cache return `folder: "drafts"`. ⚠️ Falls through to OFW for unread inbox messages, which marks them read AND stamps a "First Viewed" time the co-parent can see — irreversible. Pass `allowMarkRead:false` to refuse that fetch instead; cached, sent and already-read messages are unaffected. |
|
|
97
97
|
| `ofw_send_message(draftId?, subject?, body?, recipientIds?, replyToId?, expectedRevision?, deleteDraftOnSuccess?, myFileIDs?, force?)` | Send a message — **the one irreversible operation**. Preferred path: pass `draftId` (+ `expectedRevision`) to send an existing draft **as it exists on the server** — the tool re-reads it from OFW first and refuses if it changed since you read it (or was already sent/deleted); `subject`/`body` become optional overrides. `recipientIds` is usually still required: OFW does not store recipients on drafts. After a **confirmed** send the draft is auto-deleted (`deleteDraftOnSuccess:false` to keep it); on any failure or ambiguity it is retained and the response says why (`draftRetained`). Response leads with `sentMessageId`, `draftKey`, `threaded`, `draftDeleted`. Compose from scratch by passing `subject`/`body`/`recipientIds` with no `draftId`. |
|
|
98
|
-
| `ofw_get_unread_sent(page?, size?, autoRefresh?)` | Sent messages your co-parent hasn't read yet (from cache).
|
|
99
|
-
| `ofw_list_drafts(page?, size?, verify?, autoRefresh?)` | List saved drafts, **auto-verified**: when the cache is not verified-fresh a cheap drafts sync runs first (default `verify:true`), so one call answers server-confirmed. `verify:false` serves straight from cache. Each draft carries `serverConfirmed`, `revision` and `draftKey`. Returns `complete` — **check it before saying "you have N drafts"**. See [Freshness](#freshness). |
|
|
98
|
+
| `ofw_get_unread_sent(page?, size?, autoRefresh?)` | Sent messages your co-parent hasn't read yet (from cache). Leads with `complete`/`hasMore`/`nextPage`, then `scanned`/`total`, then `unread`; an empty sent cache that is not fresh is refused rather than reported as "nothing sent". |
|
|
99
|
+
| `ofw_list_drafts(page?, size?, verify?, autoRefresh?)` | Leads with `complete`/`hasMore`/`nextPage`; `drafts` comes last. List saved drafts, **auto-verified**: when the cache is not verified-fresh a cheap drafts sync runs first (default `verify:true`), so one call answers server-confirmed. `verify:false` serves straight from cache. Each draft carries `serverConfirmed`, `revision` and `draftKey`. Returns `complete` — **check it before saying "you have N drafts"**. See [Freshness](#freshness). |
|
|
100
100
|
| `ofw_save_draft(subject, body, recipientIds?, messageId?, replyToId?, myFileIDs?, expectedRevision?, force?)` | Create a new draft. Pass `messageId` to **replace** an existing draft: the tool creates a fresh draft and deletes the old one (OFW's update-in-place endpoint silently no-ops). The returned `id` is the NEW id; the response leads with `draftKey`, which stays the same across every edit — **track that, not the id**. Note: OFW does **not** store recipients on drafts — `recipientIds` are accepted but come back empty (a one-line NOTE says so; supply them at send time instead). Threading warnings fire only on genuine drops — a draft echoing `inReplyTo`/`showContext` IS threaded. |
|
|
101
101
|
| `ofw_delete_draft(messageId)` | Delete a draft. |
|
|
102
102
|
| `ofw_upload_attachment(path, shareClass?, label?, description?)` | Upload a local file to My Files; returns a fileId to pass into `myFileIDs`. |
|
|
@@ -133,6 +133,7 @@ Message and draft reads come from a local cache, so **a result can be stale with
|
|
|
133
133
|
|---|---|
|
|
134
134
|
| How old is this data? | `freshness` — `staleness` (`fresh`/`unverified`/`stale`), `asOf`, `ageSeconds`, a quotable `warning` |
|
|
135
135
|
| Is this the WHOLE answer? | `complete` on `ofw_list_messages` / `ofw_list_drafts` / `ofw_get_unread_sent` / `ofw_status` |
|
|
136
|
+
| If not, how do I get the rest? | `nextPage` (message tools) or `nextStart` (`ofw_list_expenses` / `ofw_list_journal_entries`) — null means there is no more |
|
|
136
137
|
| Is this entity still what I think it is? | `state` from `ofw_status` / `ofw_check_freshness` |
|
|
137
138
|
|
|
138
139
|
Rules:
|