← Back to search

io.github.chrischall/ofw-mcp

chrischall Scanned 8h ago

OurFamilyWizard co-parenting for Claude — messages, calendar, expenses, and journal

B
85.7 / 100

Versions

2.0.9latest
first seen May 19, 2026
PermissionsTool SafetyAuthAnnotationsCode QualityStabilitySpecVuln HistoryAuthorTransparencyCommunity

Tools 24

ofw_list_message_folders
annotations: verified low

List OurFamilyWizard message folders (inbox, sent, etc.) and their unread counts. Fetched LIVE from OFW, so the counts are current. Returns folder IDs needed to call ofw_list_messages. Does NOT return message content.

q string page number size number sort enum since string until string folderId string autoRefresh boolean
readOnlyHint true
ofw_list_messages
annotations: verified low

List messages from the local OurFamilyWizard cache. Supports filtering by folder, date range, and a substring query on subject+body. Pagination is offset-based (1-based `page`) but if you know what you want (a date range, a topic), prefer the filters over walking pages — the cache may have 1000+ messages. Results are newest-first by default; `sort:"oldest"` starts at the old end of a range instead of paging to it. Returns an explicit `complete` boolean describing the RESULT SET: true means "this is every message on OurFamilyWizard matching these filters as of freshness.asOf" — check it before asserting a count. An empty result from a cache that is not verified-fresh is REFUSED (result:"UNVERIFIED_EMPTY") rather than reported as an absence; pass autoRefresh:true to sync and answer instead.

q string page number size number sort enum since string until string folderId string autoRefresh boolean
readOnlyHint false
ofw_get_message
annotations: verified low

Get a single OurFamilyWizard message OR draft by ID. Reads from local cache when available; otherwise fetches from OFW — and for an UNREAD INBOX message that fetch marks it read and stamps a "First Viewed" time the co-parent can see, which is part of the record and cannot be undone. Pass allowMarkRead:false to refuse such a fetch instead (cached bodies, sent messages and already-read messages are unaffected, because none of them stamp anything). For ids that match a draft (in the drafts cache), the response carries folder="drafts" and the body/subject/recipients reflect the drafts cache (which ofw_sync_messages keeps fresh) — drafts have no `fromUser`, and `sentAt`/`fetchedBodyAt` mirror the draft\'s `modifiedAt`. For inbox/sent messages, folder is "inbox" or "sent" as before.

messageId string allowMarkRead boolean
readOnlyHint false
ofw_list_expenses
annotations: verified low

List OurFamilyWizard expenses. Offset-paged via start/max. The response leads with its paging state — `hasMore` and `nextStart` (null when the list is exhausted) — BEFORE the records, so a truncated or partially-read response still says whether more remain. Never state an expense total or an absence from one page.

max number start number
readOnlyHint true destructiveHint false
ofw_create_expense
annotations: verified low

Log a new expense in OurFamilyWizard

amount number description string
destructiveHint false
ofw_send_message
annotations: verified low

Send a message via OurFamilyWizard — the ONE irreversible operation here, so it carries the strongest guard. TO SEND AN EXISTING DRAFT (the safe default): pass draftId (or messageId — same thing). The tool re-reads the draft from OFW and sends the SERVER\'S version, so what goes out is what is on OurFamilyWizard, not what this session remembers — subject/body act only as explicit overrides. It is guarded exactly like ofw_save_draft: pass expectedRevision to assert which version you are sending; if the draft changed on OFW since you read it — or no longer exists (it may already have been SENT) — the send is REFUSED with the current server content echoed back, and nothing goes out. RECIPIENTS: OurFamilyWizard does not persist recipients on drafts, so recipientIds is usually still required at send time (ids from ofw_get_profile). After the send is CONFIRMED (OFW returned the new message id and the re-fetched sent record matches what was posted), the source draft is deleted automatically; pass deleteDraftOnSuccess:false to keep it. On ANY failure or ambiguity the draft is never deleted — the response carries draftRetained:true with the reason. TO COMPOSE FROM SCRATCH: supply subject/body/recipientIds with no draftId. If replyToId is provided (or inherited from the draft), the cache may rewrite it to the latest reply in the same thread (a note is included when this happens). ATTACHMENTS: when sending by draftId, the server draft\'s own attachments carry over automatically; myFileIDs (from ofw_upload_attachment) overrides or attaches files on a fresh compose. The response leads with sentMessageId and the stable draftKey, and reports threaded (whether OFW actually linked the reply) and draftDeleted.

destructiveHint true
ofw_list_drafts
annotations: verified low

List draft messages, verified against OurFamilyWizard in ONE call: when the local drafts cache is not verified-fresh, a cheap drafts sync runs first by default (verify:true), so the answer is server-confirmed without a second call. Pass verify:false to answer purely from the cache (no OFW requests). Returns an explicit `complete` boolean describing the RESULT SET: true means "these are ALL the drafts on OurFamilyWizard as of freshness.asOf" — check it before saying "you have N drafts". Each draft carries its `draftKey` (stable across the create-then-delete churn of editing) when one is known. An empty result from a cache that is not verified-fresh is REFUSED (result:"UNVERIFIED_EMPTY"); pass autoRefresh:true to sync and answer instead.

page number size number verify boolean autoRefresh boolean
readOnlyHint false
ofw_save_draft
annotations: verified low

Save a message as a draft in OurFamilyWizard. RECIPIENTS: OurFamilyWizard does NOT persist recipients on drafts — recipientIds are accepted but the saved draft comes back with none (documented OFW behavior, noted once in the response, not warned about; supply recipientIds at send time instead). IDENTITY: the response leads with `draftKey`, the stable identity that survives editing — key off it, because the `id` changes on EVERY edit (replacing a draft creates a NEW draft and deletes the old one; OFW\'s update-in-place endpoint silently no-ops, so we never use it). Pass messageId to replace an existing draft; the response.id will be the NEW id, and a transparency NOTE documents the swap and which fields were carried over. THREADING: if replyToId is provided, the cache may rewrite it to the latest reply in the thread (note included). The threading verdict is read from OFW\'s full echo (replyToId/inReplyTo/showContext) — a warning appears ONLY when the reply linkage was genuinely dropped or re-targeted, and the response\'s top-level replyToId/inReplyTo always agree with its listData. Attach files via myFileIDs (from ofw_upload_attachment). After saving, the tool re-fetches the draft from OFW, and the returned `revision` reflects that authoritative state (so it will match on your next edit). SAFETY: because replacing DESTROYS the old draft rather than merging, passing messageId first re-reads that draft from OFW and REFUSES the write if its subject/body/recipients changed since you read it (drafts edited in the OFW web app do not bump any timestamp, so the local cache can be silently behind). A pure replyToId normalization by OFW is NOT treated as a conflict. The refusal returns the current server body under serverBody — merge your edit into it and retry with expectedRevision.

readOnlyHint false
ofw_delete_draft
annotations: verified low

Delete a draft message from OurFamilyWizard. Also removes the draft from the local cache. Before deleting, the draft is re-read from OFW and the delete is REFUSED if it changed since you last read it (the current server body is returned so nothing is lost) — pass expectedRevision to assert which version you mean, or force:true to delete regardless.

force boolean messageId number expectedRevision string
readOnlyHint false destructiveHint true
ofw_get_unread_sent
annotations: verified low

List sent messages that have not been read by one or more recipients. Reads from local cache. Returns `complete` describing whether every sent message was scanned. An empty SENT cache that is not verified-fresh is REFUSED (result:"UNVERIFIED_EMPTY") rather than reported as "nothing sent"; pass autoRefresh:true to sync and answer instead.

page number size number autoRefresh boolean
readOnlyHint false
ofw_upload_attachment
annotations: verified low

Upload a local file to OurFamilyWizard\'s "My Files" so it can be attached to a message. Returns the fileId — pass that to ofw_send_message or ofw_save_draft in myFileIDs to attach it. The file is uploaded as PRIVATE (visible only to you) by default; pass shareClass:"SHARED" to share with co-parents directly via the My Files area.

path string label string shareClass enum description string
destructiveHint false
ofw_list_events
annotations: verified low

List OurFamilyWizard calendar events in a date range

endDate string detailed boolean startDate string
readOnlyHint true destructiveHint false
ofw_create_event
annotations: verified low

Create a calendar event in OurFamilyWizard. Unless privateEvent is true, the event is immediately visible to the co-parent — there is no draft stage.

title string
destructiveHint false
ofw_download_attachment
annotations: verified low

Download an OFW message attachment by fileId and return content you can actually read. Inline delivery walks a ladder and returns the first rung that works: (1) host-renderable images (PNG/JPEG/GIF/WEBP) come back as ImageContent; (2) .xlsx/.csv/.tsv, .pdf, .docx, .pptx and text files come back as EXTRACTED CONTENT — per-sheet CSV, per-page/slide text, document text — in the response JSON under `extracted`; (3) anything else comes back as an EmbeddedResource blob of the raw bytes. The meta block names the rung as `deliveredVia` and, when it falls through to bytes, lists what was tried in `deliveryAttempts`. Reported mime types are always normalized to a bare media type (no charset/name parameters). In disk mode the bytes are saved to ~/Downloads/ofw-mcp/ and the response carries the absolute path; pass extract:true to ALSO get the extracted content in that response. The default for `inline` can be flipped server-side via the OFW_INLINE_ATTACHMENTS env var. On a hosted deployment with no filesystem, disk mode is unavailable, so inline is forced (forcedInline:true) rather than failing — a saveTo path never costs you the content. fileId comes from attachments[].fileId on ofw_get_message. Override disk destination with OFW_ATTACHMENTS_DIR or saveTo. Re-downloading to the same path is a no-op (disk mode only).

readOnlyHint false
ofw_sync_messages
annotations: verified low

Sync messages from OurFamilyWizard into the local cache. Returns counts per folder and a list of unread inbox messages whose bodies were NOT fetched (to avoid mark-as-read on OFW). Call ofw_get_message(id) on those to read them. EVERY call re-checks the newest page first, so new messages are picked up promptly even while an old-history backfill is still running; only then does it spend what is left of its budget advancing that backfill. Pass deep:true to walk all OFW pages instead of stopping at the first all-cached page (use to backfill suspected gaps). Sync is BOUNDED and RESUMABLE: on hosted deployments a per-call OFW-request budget (env OFW_SYNC_MAX_REQUESTS, or the maxRequests argument) caps how far one call walks; when the budget is hit the response reports done:false with a note — call again with the SAME arguments to resume. done:false means older history is still being backfilled; it does NOT mean recent messages are missing. Local installs are unbounded by default (done is always true).

deep boolean folders array maxRequests number fetchUnreadBodies boolean
readOnlyHint false
ofw_check_freshness
annotations: verified low

Cheaply confirm whether the local cache still matches OurFamilyWizard, WITHOUT running a full sync. Use this before asserting anything about current state — especially "draft X is still sitting unsent". Costs one OFW request for the folder check plus one per messageId. For each folder it returns the live server count next to the cached count. For each id it returns a LIVE lifecycle `state` — "draft" | "sent" | "received" | "deleted" | "unknown" — alongside `folder`, `sentAt`, `existsOnServer` and a content comparison. `state` is the field that answers "is this still a draft?": a draft that has been SENT still exists on the server, so existsOnServer:true never distinguished the two. A cached draft whose state is no longer "draft" reports inSync:false even when its text is byte-identical. Content is compared by revision hash, because OFW draft timestamps do NOT change when a draft is edited in the web app. Does not fetch bodies into the cache, does not touch attachments, and does not depend on sync state. For draftKeys, or a full live draft inventory, use ofw_status.

folders array messageIds array allowMarkRead boolean
readOnlyHint false
ofw_status
annotations: verified low

ONE live call that answers "where does everything stand?". This is the call that should back any status summary about drafts or specific messages — never session memory, and never a cached read alone. With no arguments it returns the FULL current draft inventory, verified against OurFamilyWizard. Pass ids and/or draftKeys to get each one\'s live lifecycle `state` ("draft" | "sent" | "received" | "deleted" | "unknown") with `sentAt` and `viewedAt`. A draftKey is the stable identity ofw_save_draft returns: editing a draft mints a new OFW id every time (create-then-delete), so the key is the only way to ask "what happened to the thing I was working on?" — it resolves to the chain\'s current id and keeps resolving after the draft is SENT (state:"sent" with sentMessageId). The top-level `complete` is true ONLY when every part of this snapshot was verified live; if it is false, do not state a draft count or a lifecycle claim from this payload.

ids array draftKeys array allowMarkRead boolean includeDraftInventory boolean
readOnlyHint false
ofw_list_journal_entries
annotations: verified low

List OurFamilyWizard journal entries. Offset-paged via start/max (1-based). The response leads with its paging state — `hasMore` and `nextStart` (null when the list is exhausted) — BEFORE the records, so a truncated or partially-read response still says whether more remain. Never state an entry count or an absence from one page.

max number start number
readOnlyHint true destructiveHint false
ofw_create_journal_entry
annotations: verified low

Create a new journal entry in OurFamilyWizard

body string title string
destructiveHint false
ofw_get_profile
annotations: verified low

Get current user and co-parent profile information from OurFamilyWizard

readOnlyHint true
ofw_get_notifications
annotations: verified low

Get OurFamilyWizard dashboard summary: unread message count, upcoming events, outstanding expenses. Note: updates your last-seen status.

readOnlyHint false
ofw_get_expense_totals
annotations: verified low

Get OurFamilyWizard expense summary totals (owed/paid)

max number start number
readOnlyHint true destructiveHint false
ofw_update_event
annotations: verified low

Update an existing OurFamilyWizard calendar event. Fetches the event, applies the given changes, and writes the merged result back (OFW has no partial update).

title string eventId string children array
destructiveHint true
ofw_delete_event
annotations: verified low

Delete an OurFamilyWizard calendar event

eventId string includeFuture boolean
destructiveHint true

Permissions 2

network medium
Server uses network capabilities via: fetch()
env_vars low
Server uses env_vars capabilities via: process.env

Scan Findings 68

info
Tool 'ofw_status' annotations are consistent annotation_checker · 80%
info
Tool 'ofw_list_message_folders' annotations are consistent annotation_checker · 80%
info
Tool 'ofw_list_messages' annotations are consistent annotation_checker · 80%
info
Tool 'ofw_get_message' annotations are consistent annotation_checker · 80%
info
Tool 'ofw_send_message' annotations are consistent annotation_checker · 80%
info
Tool 'ofw_list_drafts' annotations are consistent annotation_checker · 80%
info
Tool 'ofw_save_draft' annotations are consistent annotation_checker · 80%
info
Tool 'ofw_delete_draft' annotations are consistent annotation_checker · 80%
info
Tool 'ofw_get_unread_sent' annotations are consistent annotation_checker · 80%
info
Tool 'ofw_upload_attachment' annotations are consistent annotation_checker · 80%
info
Tool 'ofw_download_attachment' annotations are consistent annotation_checker · 80%
info
Tool 'ofw_sync_messages' annotations are consistent annotation_checker · 80%
info
Tool 'ofw_check_freshness' annotations are consistent annotation_checker · 80%
info
Tool 'ofw_list_journal_entries' annotations are consistent annotation_checker · 80%
info
Tool 'ofw_create_journal_entry' annotations are consistent annotation_checker · 80%
info
Tool 'ofw_get_profile' annotations are consistent annotation_checker · 80%
info
Tool 'ofw_get_notifications' annotations are consistent annotation_checker · 80%
info
Tool 'ofw_get_expense_totals' annotations are consistent annotation_checker · 80%
info
Tool 'ofw_list_expenses' annotations are consistent annotation_checker · 80%
info
Tool 'ofw_create_expense' annotations are consistent annotation_checker · 80%
info
Tool 'ofw_list_events' annotations are consistent annotation_checker · 80%
info
Tool 'ofw_create_event' annotations are consistent annotation_checker · 80%
info
Tool 'ofw_update_event' annotations are consistent annotation_checker · 80%
info
Tool 'ofw_delete_event' annotations are consistent annotation_checker · 80%
info
Sandbox failed to start for behavioral verification behavioral_verifier · 100%
medium
Vulnerable dependency: esbuild@0.28.0 (GHSA-g7r4-m6w7-qqqr) dependency_analyzer · 95%
medium
Buffer.from base64 in chrischall-ofw-mcp-dff5c60/tests/tools/messages.test.ts:2632 entropy_analyzer · 75%
medium
Buffer.from base64 in chrischall-ofw-mcp-dff5c60/tests/tools/messages.test.ts:2653 entropy_analyzer · 75%
medium
Buffer.from base64 in chrischall-ofw-mcp-dff5c60/tests/tools/messages.test.ts:2887 entropy_analyzer · 75%
medium
Buffer.from base64 in chrischall-ofw-mcp-dff5c60/tests/tools/messages.test.ts:2931 entropy_analyzer · 75%
medium
Buffer.from base64 in chrischall-ofw-mcp-dff5c60/tests/tools/messages.test.ts:2953 entropy_analyzer · 75%
medium
Buffer.from base64 in chrischall-ofw-mcp-dff5c60/tests/tools/messages.test.ts:2993 entropy_analyzer · 75%
medium
Buffer.from base64 in chrischall-ofw-mcp-dff5c60/tests/tools/messages.test.ts:3037 entropy_analyzer · 75%
medium
Buffer.from base64 in chrischall-ofw-mcp-dff5c60/tests/tools/messages.test.ts:3226 entropy_analyzer · 75%
medium
Buffer.from base64 in chrischall-ofw-mcp-dff5c60/tests/tools/messages.test.ts:3239 entropy_analyzer · 75%
medium
Buffer.from base64 in chrischall-ofw-mcp-dff5c60/tests/tools/messages.test.ts:3252 entropy_analyzer · 75%
info
package.json metadata manifest_parser · 100%
info
Tool: ofw_list_message_folders manifest_parser · 85%
info
Tool: ofw_list_messages manifest_parser · 85%
info
Tool: ofw_get_message manifest_parser · 85%
info
Tool: ofw_send_message manifest_parser · 85%
info
Tool: ofw_list_drafts manifest_parser · 85%
info
Tool: ofw_save_draft manifest_parser · 85%
info
Required env vars (18) manifest_parser · 80%
info
Tool: ofw_delete_draft manifest_parser · 85%
info
Tool: ofw_get_unread_sent manifest_parser · 85%
info
Tool: ofw_upload_attachment manifest_parser · 85%
info
Tool: ofw_download_attachment manifest_parser · 85%
info
Tool: ofw_sync_messages manifest_parser · 85%
info
Tool: ofw_check_freshness manifest_parser · 85%
info
Sandbox failed to start for output poisoning scan output_poisoning · 100%
info
Tool: ofw_status manifest_parser · 85%
info
Tool: ofw_list_journal_entries manifest_parser · 85%
info
Tool: ofw_create_journal_entry manifest_parser · 85%
info
Tool: ofw_get_profile manifest_parser · 85%
info
Tool: ofw_get_notifications manifest_parser · 85%
info
Tool: ofw_get_expense_totals manifest_parser · 85%
info
Tool: ofw_list_expenses manifest_parser · 85%
info
Tool: ofw_create_expense manifest_parser · 85%
info
Tool: ofw_list_events manifest_parser · 85%
info
Tool: ofw_create_event manifest_parser · 85%
info
Tool: ofw_update_event manifest_parser · 85%
info
Tool: ofw_delete_event manifest_parser · 85%
medium
Permission: network access detected permission_analyzer · 70%
low
Permission: env_vars access detected permission_analyzer · 90%
critical
Tool poisoning in 'ofw_save_draft': Directive language: 'never' poisoning · 85%
info
SBOM generated: 222 components sbom_generator · 100%
medium
No build provenance detected (SLSA L0) slsa_assessor · 90%