ofw-mcp 2.19.5 → 2.20.1
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 +25 -2
- package/dist/auth-password.js +17 -1
- package/dist/bundle.js +1502 -407
- package/dist/client.js +66 -20
- package/dist/config.js +37 -0
- package/dist/index.js +13 -9
- package/dist/tool-surface.js +14 -0
- package/dist/tools/_shared.js +5 -1
- package/dist/tools/attachments.js +32 -32
- package/dist/tools/expenses.js +563 -56
- package/dist/tools/messages.js +1 -1
- package/package.json +4 -4
- package/server.json +14 -2
- package/skills/ofw/SKILL.md +10 -3
- package/skills/ofw-fpx/references/requests.md +58 -4
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
},
|
|
7
7
|
"metadata": {
|
|
8
8
|
"description": "OurFamilyWizard tools for Claude Code",
|
|
9
|
-
"version": "2.
|
|
9
|
+
"version": "2.20.1"
|
|
10
10
|
},
|
|
11
11
|
"plugins": [
|
|
12
12
|
{
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
"displayName": "OurFamilyWizard",
|
|
15
15
|
"source": "./",
|
|
16
16
|
"description": "OurFamilyWizard co-parenting tools for Claude — messages, calendar, expenses, and journal via MCP",
|
|
17
|
-
"version": "2.
|
|
17
|
+
"version": "2.20.1",
|
|
18
18
|
"author": {
|
|
19
19
|
"name": "Chris Chall"
|
|
20
20
|
},
|
package/README.md
CHANGED
|
@@ -151,7 +151,10 @@ Read-only tools run automatically. Writes that reach your co-parent or the court
|
|
|
151
151
|
| `ofw_delete_event` | Delete a calendar event | Confirm (server) if shared | `all` (or `drafts` + `OFW_CALENDAR_WRITES`) |
|
|
152
152
|
| `ofw_get_expense_totals` | Expense summary totals | Auto | any |
|
|
153
153
|
| `ofw_list_expenses` | Expense history | Auto | any |
|
|
154
|
-
| `
|
|
154
|
+
| `ofw_list_expense_categories` | Expense category ids and split metadata | Auto | any |
|
|
155
|
+
| `ofw_upload_expense_pdf` | Upload a receipt PDF to My Files for an expense (SHARED — the co-parent sees it in My Files at once, even on a private expense) | Confirm (server) | `all` |
|
|
156
|
+
| `ofw_create_expense` | Log a new expense; supports private entries and one receipt PDF | Confirm (server) | `all` |
|
|
157
|
+
| `ofw_update_expense` | Change an expense, e.g. publish a private one. Pass only the fields to change; it reads the expense first so the rest are kept | Confirm (server) | `all` |
|
|
155
158
|
| `ofw_list_journal_entries` | Journal entries | Auto | any |
|
|
156
159
|
| `ofw_create_journal_entry` | Create a journal entry | Confirm | `all` |
|
|
157
160
|
|
|
@@ -232,7 +235,7 @@ Previews name what you are approving — recipients by name, subject and full bo
|
|
|
232
235
|
|---|---|---|
|
|
233
236
|
| `MCP_CONFIRM_MODE` | `ask-user` | What a gated write does on a client that cannot show a prompt. `ask-user`: two steps, and the model must get your approval in chat before using the token. `auto`: two steps, but the model may use the token after reviewing the preview itself. `refuse`: such writes are refused (do them on ourfamilywizard.com). An unrecognised value is treated as `refuse`. |
|
|
234
237
|
| `MCP_CONFIRM_TTL_SECONDS` | `600` | How long a token stays valid. |
|
|
235
|
-
| `MCP_CONFIRM_SECRET` | random per process | Signing key; set it only if tokens must survive a server restart. |
|
|
238
|
+
| `MCP_CONFIRM_SECRET` | random per process | Signing key; set it only if tokens must survive a server restart. On mcp-host the host supplies a stable per-child key (`MCP_HOST_CONFIRM_SECRET`) and spent tokens are recorded under `MCP_DATA_DIR`, so an approval survives an idle restart. |
|
|
236
239
|
|
|
237
240
|
Private events and `PRIVATE` uploads — which the co-parent never sees — are not gated. `OFW_WRITE_MODE` below stays the structural layer underneath: a tool your write mode excludes does not exist at all.
|
|
238
241
|
|
|
@@ -250,6 +253,26 @@ The host's "Confirm" permission above is a *hint* to the MCP host — a host con
|
|
|
250
253
|
|
|
251
254
|
Unrecognized values fail closed to `none`, with a warning on stderr — a typo never silently grants write access.
|
|
252
255
|
|
|
256
|
+
#### Expense-only deployments
|
|
257
|
+
|
|
258
|
+
For a dedicated reimbursement integration, you can also structurally remove unrelated OFW capabilities:
|
|
259
|
+
|
|
260
|
+
| Setting | Registered surface |
|
|
261
|
+
|---|---|
|
|
262
|
+
| `OFW_EXPENSE_ONLY=true` | `ofw_healthcheck` plus the expense tools only. Profile/dashboard, messages, calendar, and journal tools do not exist. |
|
|
263
|
+
| `OFW_EXPENSE_UPLOAD_ONLY=true` | Strictest mode: `ofw_healthcheck`, `ofw_upload_expense_pdf`, `ofw_create_expense`, and `ofw_update_expense` only. Expense totals/listing are removed too. This flag implies `OFW_EXPENSE_ONLY`. |
|
|
264
|
+
|
|
265
|
+
These flags are registration-time restrictions, not prompt instructions. A host cannot call a tool that was never registered. Unrecognized non-empty flag values fail closed to the restricted state. `OFW_WRITE_MODE` still applies underneath; for the strict upload workflow set `OFW_WRITE_MODE=all` so expense creation is available.
|
|
266
|
+
|
|
267
|
+
Recommended reimbursement-only deployment:
|
|
268
|
+
|
|
269
|
+
```env
|
|
270
|
+
OFW_EXPENSE_UPLOAD_ONLY=true
|
|
271
|
+
OFW_WRITE_MODE=all
|
|
272
|
+
OFW_ALLOW_MARK_READ=false
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
|
|
253
276
|
### Reading is a write, too (`OFW_ALLOW_MARK_READ`)
|
|
254
277
|
|
|
255
278
|
Fetching a message body for the first time marks it read on OurFamilyWizard and stamps a **"First Viewed" timestamp your co-parent can see**. That is part of the record and cannot be undone — and it happens as a side effect of an ordinary read, so `OFW_WRITE_MODE` does not govern it.
|
package/dist/auth-password.js
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
// `resolveAuth()` in `./auth.ts` can call it without a Client instance, and
|
|
11
11
|
// so tests can mock it at the module boundary.
|
|
12
12
|
import { createHash } from 'node:crypto';
|
|
13
|
-
import { currentCallSignal } from '@chrischall/mcp-utils';
|
|
13
|
+
import { EdgeBlockedError, currentCallSignal, detectEdgeBlock } from '@chrischall/mcp-utils';
|
|
14
14
|
import { BASE_URL, OFW_PROTOCOL_HEADERS, OFW_TOKEN_TTL_MS, assertOfwUrl } from './protocol.js';
|
|
15
15
|
/**
|
|
16
16
|
* OFW definitively refused this username/password (it re-rendered its login
|
|
@@ -86,6 +86,22 @@ export async function loginWithPassword(username, password) {
|
|
|
86
86
|
}).toString(),
|
|
87
87
|
});
|
|
88
88
|
if (!response.ok) {
|
|
89
|
+
// A CDN/WAF refusal page is not OFW judging the password: name it, so the
|
|
90
|
+
// healthcheck reports edge_blocked instead of a missing credential, and
|
|
91
|
+
// nothing is latched as rejected (chrischall/mcp-host#1015). The page
|
|
92
|
+
// itself stays out of the message.
|
|
93
|
+
const edge = detectEdgeBlock({
|
|
94
|
+
body: await response.text().catch(() => ''),
|
|
95
|
+
headers: response.headers,
|
|
96
|
+
status: response.status,
|
|
97
|
+
});
|
|
98
|
+
if (edge !== null) {
|
|
99
|
+
throw new EdgeBlockedError(response.status, edge.vendor, {
|
|
100
|
+
service: 'OurFamilyWizard',
|
|
101
|
+
method: 'POST',
|
|
102
|
+
path: '/ofw/login',
|
|
103
|
+
});
|
|
104
|
+
}
|
|
89
105
|
throw new Error(`OFW login failed: ${response.status} ${response.statusText}`);
|
|
90
106
|
}
|
|
91
107
|
const contentType = response.headers.get('content-type') ?? '';
|