OurFamilyWizard MCP
An MCP server that gives Claude natural-language access to your own OurFamilyWizard co-parenting account — reading messages, calendar, expenses, and journal, and performing guarded writes.
Account & health — get your profile/co-parent info, dashboard notification counts, and verify credentials/upstream reachability (
ofw_healthcheck).Messages — list folders and messages, read a message or draft by ID, sync to a local SQLite cache, list unread sent messages, send messages, and create/update/delete drafts.
Attachments — upload local files to OFW "My Files" (PRIVATE or SHARED) and download message attachments inline as images, extracted text (PDF/Office/CSV), or raw bytes.
Calendar — list events in a date range and create, update, or delete private or co-parent-visible events.
Expenses — view totals and categories, list expenses, create/update expenses (private or shared), and upload receipt PDFs.
Journal — list and create journal entries.
Freshness & status verification —
ofw_check_freshnessandofw_statusgive live, server-confirmed state (e.g. whether a draft is still a draft or was sent), with explicitcomplete/freshnessmarkers and refusal to report absence from unverified caches.Safety controls — server-side confirmation (preview + token) for court-visible writes, structural
OFW_WRITE_MODEgating, and mark-read protection so reads don't silently stamp a co-parent-visible "First Viewed" time.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@OurFamilyWizard MCPShow me my recent OFW messages"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
OurFamilyWizard MCP
A Model Context Protocol server that connects Claude to OurFamilyWizard, giving you natural-language access to your co-parenting messages, calendar, expenses, and journal.
AI-developed project. This codebase was entirely built and is actively maintained by Claude Sonnet 4.6. No human has audited the implementation. Review all code and tool permissions before use.
What you can do
Ask Claude things like:
"Show me my recent OFW messages"
"What's on the kids' calendar next week?"
"List recent expenses and tell me what I owe"
"Add a journal entry about today's pickup"
"Draft a reply to the last message from my co-parent"
Related MCP server: whoop-mcp
Requirements
Node.js 22.5 or later (
node:sqliteis the cache backend)An active OurFamilyWizard account
Acknowledgement of Terms
By using this MCP server, you acknowledge and agree to the following:
1. This server accesses your own OurFamilyWizard account. Auth happens via your own credentials. It does not — and cannot — access your co-parent's account, your children's accounts, or anyone else's.
2. OurFamilyWizard's Terms govern your use of this server, just as they govern your direct use of OFW. There is no explicit anti-scraping clause; the governing language is broader:
Users may not obtain or attempt to obtain any materials or information through any means not intentionally made available.
And on credentials: "You are solely responsible for (1) maintaining the strict confidentiality of assigned Authentication Methods, (2) instructing any individual to whom the assigned Authentication Method is shared ('Authorized User') to not allow another person to use the Authentication Method." OFW does contemplate "Authorized Users" and third-party-enabled integrations — but the account holder remains responsible.
You are agreeing to those terms — read by the maintainer 2026-05-23 — every time you invoke a tool in this server.
3. Personal, family use only. This project is not affiliated with, endorsed by, sponsored by, or in partnership with OurFamilyWizard, LLC or its parent. It is a personal automation tool for the named account holder. Do not use it on behalf of a co-parent without their consent, do not share credentials with anyone, and do not use it to bulk-extract another family's data.
4. OFW is a court-of-record platform. Messages, expenses, calendar entries, and journal entries on OFW may be entered into legal proceedings — including custody, divorce, and parenting-plan-modification cases. Anything this server writes to OFW (drafts you save, events you create, expenses you log) will appear with the same legal weight as if you had typed it yourself. Do not let this MCP send a message, create an event, or log an expense that you have not read and approved. Review every write operation before confirming.
5. You accept full responsibility for any consequences — both technical (account warnings, suspension) and legal (anything OFW records about your account activity). The MCP author is not your attorney; if you're using OFW in connection with an active legal matter, talk to your actual attorney before automating anything.
This section is the maintainer's good-faith summary of the terms — it is not legal advice and does not modify or supersede OurFamilyWizard's actual ToS.
Installation
1. Clone and build
git clone https://github.com/chrischall/ofw-mcp.git
cd ofw-mcp
npm install
npm run build2. Add to Claude Desktop
Edit your Claude Desktop config file:
Mac:
~/Library/Application Support/Claude/claude_desktop_config.jsonWindows:
%APPDATA%\Claude\claude_desktop_config.json
Add the ofw entry inside "mcpServers" (create the key if it doesn't exist):
{
"mcpServers": {
"ofw": {
"command": "node",
"args": ["/absolute/path/to/ofw-mcp/dist/index.js"],
"env": {
"OFW_USERNAME": "your-email@example.com",
"OFW_PASSWORD": "your-ofw-password"
}
}
}
}Replace /absolute/path/to/ofw-mcp with the actual path where you cloned the repo. On Mac, run pwd inside the cloned directory to get it.
3. Restart Claude Desktop
Quit completely (Cmd+Q on Mac, not just close the window) and relaunch.
4. Verify
Ask Claude: "What does my OFW dashboard look like?" — it should show your unread message count, upcoming events, and outstanding expenses.
Authentication
ofw-mcp tries three auth paths in order; whichever succeeds first is used. Existing setups keep working unchanged.
Env-var credentials (legacy, recommended for Claude Desktop). Set
OFW_USERNAME+OFW_PASSWORDand the server logs in via OFW's form endpoint. This is the path shown in the Claude Desktop config above.fetchproxy fallback (no env vars needed). When the credentials are absent, the server reads
localStorage["auth"]once at startup from your already-signed-inourfamilywizard.comtab via the ContextMint Bridge browser extension (built on fetchproxy). After that one read, all OFW API calls go directly from Node — the extension is not in the request hot path. Install ContextMint Bridge from its releases page (Chrome: download the Chrome zip, unzip it, and load it unpacked atchrome://extensionswith Developer mode on; Safari is not available yet — it will ship inside the ContextMint app, which has no public download — so use Chrome for now), sign into OurFamilyWizard once, and the MCP just works. ContextMint Bridge is the fetchproxy browser extension under its new name, from the same maintainer — fetchproxy's own README (https://github.com/chrischall/fetchproxy#extension) points to it. Its source is public at https://github.com/nullnet-app/contextmint-bridge: build it yourself, or check a release zip against the.sha256file published beside it (shasum -a 256 -c contextmint-bridge-chrome-<version>.zip.sha256). If you have multiple OFW accounts and want them to use separate caches, setOFW_CACHE_IDENTITYto a label per profile.Error. If neither path is available, the server tells you exactly which fix to apply. Set
OFW_DISABLE_FETCHPROXY=1to skip the fetchproxy fallback entirely (turns missing credentials into a hard error — useful in headless CI).
Credential options (env-var path)
Option A — env block in Claude Desktop config (shown above, recommended):
"env": {
"OFW_USERNAME": "your-email@example.com",
"OFW_PASSWORD": "your-ofw-password"
}Option B — .env file in the project directory:
cp .env.example .env
# edit .env and fill in your credentialsEnvironment variables always take priority over the .env file. You can also pass them directly on the command line:
OFW_USERNAME=you@example.com OFW_PASSWORD=yourpass node dist/index.jsAvailable tools
Read-only tools run automatically. Writes that reach your co-parent or the court-visible record — Confirm (server) below — are confirmed by the server itself before anything is sent (see Server-side confirmation); the other writes rely on your MCP host's own approval prompt (Confirm). The Write mode column shows the minimum OFW_WRITE_MODE a tool needs to be available at all — see Write protection below.
Tool | What it does | Permission | Write mode |
| Your profile and co-parent info | Auto | any |
| Dashboard counts (unread messages, upcoming events, outstanding expenses). Updates your co-parent-visible last-seen status, so it is not marked read-only | Confirm | any |
| Folders with unread counts — get folder IDs here before listing messages | Auto | any |
| Messages in a folder | Auto | any |
| Full content of a single message | Auto | any |
| Sync messages into the local cache (unread bodies left unfetched to avoid read receipts) | Auto | any |
| Sent messages a recipient hasn't read yet (from local cache) | Auto | any |
| Cheap live check that the cache still matches OFW — per id, whether it is still a | Auto | any |
| One live call for "where does everything stand?" — the full verified draft inventory, and the current state of any ids or draft keys | Auto | any |
| Download a message attachment to disk, or inline as extracted content / bytes | Auto | any |
| Send a message | Confirm (server) |
|
| Draft messages | Auto | any |
| Create or update a draft | Confirm |
|
| Delete a draft | Confirm |
|
| Upload a local file from the upload directory ( | Auto (PRIVATE) / Confirm (server) (SHARED) |
|
| Calendar events in a date range | Auto | any |
| Create a calendar event | Confirm (server) unless private |
|
| Update a calendar event | Confirm (server) if shared |
|
| Delete a calendar event | Confirm (server) if shared |
|
| Expense summary totals | Auto | any |
| Expense history | Auto | any |
| Expense category ids and split metadata | Auto | any |
| 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) |
|
| Log a new expense; supports private entries and one receipt PDF | Confirm (server) |
|
| 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) |
|
| Journal entries | Auto | any |
| Create a journal entry | Confirm |
|
Data freshness (OFW_FRESHNESS_TTL_SECONDS)
Message and draft reads are served from the local cache, which means a result can look authoritative while being minutes or months out of date. The cache also cannot detect some changes on its own: editing a draft in the OFW web app bumps no timestamp at all, so "nothing changed" and "we didn't look" are indistinguishable unless the server says which happened.
So every read tool (ofw_list_messages, ofw_list_drafts, ofw_get_message, ofw_list_message_folders, ofw_sync_messages) returns a freshness block alongside its data:
"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."
}staleness is fresh only when the data was fetched live in that call, or verified against OFW within the threshold by a sync that actually reached that folder. It degrades to unverified when it ages out or a sync skipped the folder, and stale when the folder has never been checked at all. Anything other than fresh always carries a human-readable warning stating the age and the reason. The bias is deliberate and one-directional: a false unverified costs one extra call, whereas a false fresh lets remembered state be narrated as present fact.
Drafts additionally carry per-item cacheStatus, asOf, and serverConfirmed — true only when a completed drafts walk verified them inside the threshold. serverConfirmed: false means a draft's existence and unsent status are remembered, not known, and should not be stated as current fact without calling ofw_check_freshness first.
ofw_check_freshness is the cheap way to re-verify: one request for a folder count comparison plus one per message id, no bodies, no full sync. Draft ids are compared by content revision, not timestamp, for the reason above. It probes ids that are cached as drafts, as sent messages, or as already-read inbox messages freely, because none of those can change anything; any other id would mark an unread inbox message as read on OurFamilyWizard — an irreversible change to a court-visible record — so those are skipped unless you pass allowMarkRead: true.
Set OFW_FRESHNESS_TTL_SECONDS to tune the threshold (default 300, i.e. 5 minutes). Unusable values fall back to the default rather than widening the window.
Is it still what I think it is? (ofw_status)
Freshness answers how old is this data. It does not answer is this entity still what I think it is — and that is a different failure. A draft that has been sent still exists on the server, so "does this id exist?" comes back true for the one case where the answer matters most.
ofw_status is the call that should back any status summary:
{
"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
}stateisdraft,sent,received,deletedorunknown, read live from OFW.unknownmeans the question was not answered — it is not a synonym for "fine".With no arguments it returns the full draft inventory, verified against OFW first.
complete: truemeans every part of the snapshot was confirmed live. If it is false,incompleteReasonssays what wasn't, and the payload is not a basis for stating a count.
Draft keys. Editing a draft mints a new OFW id every time (ofw_save_draft replaces by create-then-delete, because OFW's update-in-place endpoint silently no-ops). ofw_save_draft therefore also returns a draftKey that stays constant across every edit and follows the message into Sent. ofw_status(draftKeys: ["dk_…"]) resolves it to the current id and state — including state: "sent" with sentMessageId — so "what happened to the draft I was working on?" is one call, not a guess about which id is current.
Absence is never reported from a stale cache
A cached read that comes back empty is shaped identically to a verified "nothing there". ofw_list_messages, ofw_list_drafts and ofw_get_unread_sent therefore refuse rather than answer when the result is empty and the backing cache is not fresh:
{
"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
}A false negative ("no, that was never sent") is more dangerous than a refusal, because it reads as a definitive answer. Non-empty results are never withheld — a stale cache that did find something is still evidence of presence, labelled with its age as before. Pass autoRefresh: true (or set OFW_AUTO_REFRESH=true) to have the tool sync and answer instead of refusing; a refresh that still cannot make the read verifiable refuses anyway.
Every list read also carries an explicit complete boolean describing the result set — "this is every matching item on OurFamilyWizard as of asOf" — with a completeNote naming what is missing when it is false. Check it before stating a count.
Server-side confirmation (MCP_CONFIRM_MODE)
The writes marked Confirm (server) above — sending a message, logging an expense, creating/updating/deleting a shared calendar event, and uploading a file as SHARED — are confirmed by this server, not left to the host. A client that can show a confirmation prompt (Claude Code) gets one, with a preview of exactly what would happen, unless MCP_CONFIRM_ELICITATION=off. A client that cannot (claude.ai, Claude Desktop) gets two steps: the first call writes nothing and returns that preview plus a confirmToken, and only a repeat call with the same arguments and that token proceeds.
Previews name what you are approving — recipients by name, subject and full body, the reply target, attachment file names; the expense amount and description; the event's title, date, time, visibility and (for an update) before and after. The token is bound to exactly that: a different body, amount or recipient is refused, and so is a draft or event that changed on OurFamilyWizard after the preview (say, the co-parent edited the event), even with force: true. A token works once and expires.
Variable | Default | |
|
| What a gated write does on a client that cannot show a prompt. |
|
|
|
|
| How long a token stays valid. |
| 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 ( |
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.
A write that times out is not a write that failed. If ofw_send_message, ofw_create_expense, ofw_create_event or ofw_create_journal_entry loses its connection or times out without a definitive answer from OFW, the result is SEND_UNCONFIRMED / EXPENSE_UNCONFIRMED / EVENT_UNCONFIRMED / JOURNAL_UNCONFIRMED: it may have landed. Check the matching list tool (or ourfamilywizard.com) before retrying, or the co-parent sees it twice.
Write protection (OFW_WRITE_MODE)
The host's "Confirm" permission above is a hint to the MCP host — a host configured to auto-approve tools (or a user who clicked "always allow" once) would leave nothing between model output and a sent message. Because OurFamilyWizard is a court-of-record platform, the server also supports a structural gate: set OFW_WRITE_MODE in the server's env block and tools above your chosen level are never registered, so no host setting or prompt-injected instruction can invoke them.
| What's available |
| Read/sync/search only. No write tools exist. |
| Adds draft-level writes: |
| Everything (the default — fully backward compatible). |
Unrecognized values fail closed to none, with a warning on stderr — a typo never silently grants write access.
Expense-only deployments
For a dedicated reimbursement integration, you can also structurally remove unrelated OFW capabilities:
Setting | Registered surface |
|
|
| Strictest mode: |
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.
Recommended reimbursement-only deployment:
OFW_EXPENSE_UPLOAD_ONLY=true
OFW_WRITE_MODE=all
OFW_ALLOW_MARK_READ=falseReading is a write, too (OFW_ALLOW_MARK_READ)
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.
By default nothing changes: reads behave exactly as they always have. Two controls exist if you want them:
Setting | Effect |
| Refuses a fetch that would stamp an unread inbox message, returning a structured |
| Deployment-wide ceiling. No tool may stamp: |
OFW_FETCH_UNREAD_BODIES=true flips ofw_sync_messages to fetch unread bodies by default (off unless set) — useful where read receipts are routine. It is capped by OFW_ALLOW_MARK_READ.
Calendar opt-in (OFW_CALENDAR_WRITES)
Calendar events sit between the two message tiers: they have no draft stage (a created event is immediately visible on the shared record), but unlike a sent message they are reversible — an event can be edited or deleted afterward. If you run in drafts mode but are comfortable with direct calendar writes, set OFW_CALENDAR_WRITES=true to additionally register ofw_create_event, ofw_update_event, and ofw_delete_event. The flag is redundant in all mode and never overrides none.
Egress allowlist
Every request that carries your OFW token or login passes its constructed URL through a host check before fetch — the server refuses to send them to any host other than ofw.ourfamilywizard.com. Today it always passes, and that is the point: it makes "the credential only ever goes to OFW" a structural invariant rather than a code-review promise, so a later refactor or a compromised dependency that pointed a request elsewhere throws instead of carrying your bearer token or messages off-host. It is a pre-flight check on the URL we build, not a redirect-following egress filter.
The one other host the server contacts is *.oaiusercontent.com, and only for ofw_upload_expense_pdf's url mode: it downloads the receipt PDF from that signed file URL with no OFW credential attached, re-checking every redirect hop against the same suffix, capping the size and requiring a real PDF. The hosted registration's egress allowlist (mint.yaml) names exactly these two hosts.
Troubleshooting
"0 messages" — Claude may have read the notification counts rather than the actual messages. Ask explicitly: "List the messages in my OFW inbox" or "Use ofw_list_message_folders then ofw_list_messages".
"OFW auth: set OFW_USERNAME + OFW_PASSWORD, or install ContextMint Bridge…" — neither auth path is configured. Either fill in the env block in your Claude Desktop config, or install ContextMint Bridge and sign into ourfamilywizard.com in your browser.
"fetchproxy fallback failed" / "ContextMint Bridge is down" — the env-var path wasn't configured and the extension couldn't be reached. Confirm ContextMint Bridge is installed, signed into OFW, and that it's running (open the extension popup). If you want to disable the fallback entirely, set OFW_DISABLE_FETCHPROXY=1.
"OFW login not attempted — this OurFamilyWizard email and password were already rejected" — OFW refused these credentials earlier in this session. OFW counts failed sign-ins against the account, so the server does not re-send a rejected password on every call. Update OFW_USERNAME / OFW_PASSWORD to your current login (or restart the server) and try again.
403 Forbidden — wrong credentials. Verify your username/password at ofw.ourfamilywizard.com.
Tools not appearing in Claude — go to Claude Desktop → Settings → Developer to see connected servers and any error output. Make sure you fully quit and relaunched after editing the config.
Can't find the config file on Mac — in Finder press Cmd+Shift+G and paste ~/Library/Application Support/Claude/.
Security
Credentials live only in your local config file or
.envThey are passed to the server as environment variables and never logged
The server authenticates with OFW using the same login flow as the web app
Use a strong, unique OFW password
Requests carrying your OFW credential are host-allowlisted to
ofw.ourfamilywizard.com; the only other host contacted is the receipt-download host forofw_upload_expense_pdf'surlmode (see Egress allowlist)
Local data, and how to remove it
The server keeps co-parenting data on this machine, readable only by your user account:
What | Where | Permissions |
Message cache (message bodies, recipients, drafts, attachment records) |
| dir |
Downloaded attachments |
| dirs the server creates |
Session token |
|
|
The cache keeps everything it has synced until you remove it; nothing expires it. When you no longer need it — the account is closed, the case is over, or you are handing the machine on — quit your MCP host and delete it:
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_FILEThe next start rebuilds the cache from OurFamilyWizard with ofw_sync_messages. A directory you pointed OFW_ATTACHMENTS_DIR at yourself keeps its own permissions (it may be shared on purpose); only what the server creates, and the default ~/Downloads/ofw-mcp, are locked down.
On a hosted connector (mcp-host) the same data lives in the registration's persistent data directory instead of on your machine: the message cache under $HOME/.cache/ofw-mcp/, the session token at $MCP_DATA_DIR/.ofw-mcp/session.json, the browser-bridge identity under $HOME/.fetchproxy/identity/, and spent confirm-token nonces under $MCP_DATA_DIR/.mcp-confirm/. Downloads are returned inline and never written to disk. The commands above do not reach it — how long that directory is kept, and whether disconnecting or deleting the registration removes it, is the host's retention policy, not this server's.
Development
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)Main is protected. All changes land via PR — open with gh pr create --label <release-notes-label> and add ready-to-merge once you're satisfied with the auto-review feedback. See CLAUDE.md for the full PR + release flow.
Project structure
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.spyOnAuth flow
Auth resolution lives in src/auth.ts. Three paths, in priority order:
Env vars present →
src/auth-password.tsdoes the legacy OFW Spring Security form login:GET /ofw/login.form— establishes a session cookiePOST /ofw/login— submits credentials, returns{ auth: "<token>" }
Env vars absent (and
OFW_DISABLE_FETCHPROXYunset) →@fetchproxy/bootstrapreadslocalStorage["auth"]+localStorage["tokenExpiry"]once from the user's signed-inourfamilywizard.comtab, then closes the bridge.Nothing configured → throws with both fixes spelled out.
Either path returns a Bearer token to OFWClient, which then operates from Node with Authorization: Bearer <token> — fetchproxy is not in the request hot path. On 401 the client re-resolves auth and replays once. Tokens are cached for 6h (env-var path) or until tokenExpiry (fetchproxy path).
Also see the fetchproxy README for extension install instructions.
License
MIT
Available Tools
28 toolsofw_check_freshnessADestructive
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.
| Name | Required | Description | Default |
|---|---|---|---|
| folders | No | Folders to compare cached vs live counts for. Defaults to all three when messageIds is not given. Must be non-empty if given. | |
| messageIds | No | Specific ids to verify against OFW (max 25). Ids cached as drafts, as sent messages, or as already-read inbox messages are probed freely — none of those can stamp the record. Anything else is skipped — see allowMarkRead. | |
| allowMarkRead | No | Default false. Probing an id whose cached state cannot rule out an unread INBOX message requires fetching its detail, which marks it READ on OurFamilyWizard and stamps a co-parent-visible "First Viewed" time — irreversible. Such ids are skipped (reason:"WOULD_MARK_READ") unless you set this to true. The server-wide OFW_ALLOW_MARK_READ=false is a ceiling this cannot raise. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With destructiveHint=true and readOnlyHint=false set, the description explains exactly why: probing certain ids fetches detail, marks the message READ on OFW and stamps a co-parent-visible, irreversible 'First Viewed' time, and it notes OFW_ALLOW_MARK_READ as a server-wide ceiling. It also discloses the cost model (one request per folder plus one per messageId), which no annotation conveys.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with purpose, then cost, then return semantics, then exclusions — a sensible order. It is dense and longer than average, and a few clauses (e.g. the byte-identical/inSync example) could be tightened, but nearly every sentence carries non-redundant information.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
No output schema exists, and the description compensates fully: it lists the returned fields (live vs cached count, state enum values, folder, sentAt, existsOnServer, inSync) and explains the surprising semantics of each. Nothing needed to call this tool correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3, but the description adds real meaning: it explains the per-folder live-vs-cached count comparison, the per-id lifecycle state field and why existsOnServer alone cannot distinguish draft from sent, and that content is compared by revision hash because draft timestamps do not change on edit.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a precise verb+resource: cheaply confirm whether the local cache still matches OurFamilyWizard without a full sync. It is clearly distinguished from the sync tool and from ofw_status, and the 'freshness' framing is immediately actionable.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly says when to use it ('before asserting anything about current state — especially "draft X is still sitting unsent"') and names the alternative for a different need ('For draftKeys, or a full live draft inventory, use ofw_status'). It also states what it deliberately does not do (bodies, attachments, sync state).
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ofw_create_eventADestructive
Create a calendar event in OurFamilyWizard. Unless privateEvent is true, the event is immediately visible to the co-parent — there is no draft stage — so a shared event is confirmed first (a private one is not). If the request fails without a definitive answer the result is EVENT_UNCONFIRMED: the event may already exist, so do NOT retry until ofw_list_events shows it did not land. Asks the user to confirm first: a confirmation prompt where the client supports one; otherwise the first call performs NO write and returns a preview of exactly what would happen plus a confirmToken, and only a repeat call with that token proceeds (see MCP_CONFIRM_MODE).
| Name | Required | Description | Default |
|---|---|---|---|
| notes | No | ||
| title | Yes | ||
| allDay | No | ||
| endDate | No | End date YYYY-MM-DD (default: startDate) | |
| endTime | No | End time HH:mm, 24-hour (required unless allDay) | |
| children | No | Child userIds to tag (see ofw_get_profile) | |
| location | No | ||
| startDate | Yes | Start date YYYY-MM-DD | |
| startTime | No | Start time HH:mm, 24-hour (required unless allDay) | |
| confirmToken | No | ONLY for the two-step confirmation fallback (a client without MCP elicitation). The confirmToken from this same tool's phase-1 "confirmation-required" response, passed back ONLY after the user has seen that preview and explicitly approved it in chat — never on the first call, never invented, never reused. Call again with the same arguments. Ignored when the client supports elicitation. | |
| privateEvent | No | true = visible only to you; default false = shared with co-parent | |
| eventParentId | No | userId of the parent the event is 'for' | |
| pickUpParentId | No | userId of the pick-up parent | |
| dropOffParentId | No | userId of the drop-off parent | |
| reminderMinutes | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Goes well beyond the readOnly/destructive/openWorld annotations by disclosing the confirmation protocol, the two-step confirmToken fallback, the immediate visibility of shared events, and the ambiguous EVENT_UNCONFIRMED failure mode with its retry prohibition. This is precisely the kind of safety-critical behavior an agent needs and cannot get from structured fields.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Purpose is front-loaded in the first clause, and each sentence carries non-redundant operational detail. It is dense and somewhat run-on in the confirmation/fallback sentence, but there is little waste.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a complex mutating tool with 15 params and no output schema, the description covers the crucial gaps: confirmation, idempotency/retry safety, and visibility semantics. It leaves some parameters (notes, location, reminderMinutes) unexplained, but the higher-risk behavior is addressed.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 67%, covering endDate, times, children, confirmToken, privateEvent, and the parent IDs, so most semantics live in the schema. The description adds meaning for privateEvent's visibility consequence and confirmToken's usage constraints but does not compensate for the undocumented params (notes, title, allDay, location, reminderMinutes). Baseline 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Create a calendar event in OurFamilyWizard') and implicitly contrasts with siblings like ofw_update_event/ofw_delete_event/ofw_list_events by naming ofw_list_events as the verification path. An agent knows exactly what this tool does and how it relates to the other event tools.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives strong conditional guidance: shared vs private visibility, no draft stage, do NOT retry on EVENT_UNCONFIRMED until ofw_list_events confirms absence, and the confirmation flow. It names one alternative (ofw_list_events) but does not explicitly route to ofw_update_event/ofw_delete_event for other operations, so it stops short of full when/when-not/alternatives coverage.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ofw_create_expenseADestructive
Log a new expense in OurFamilyWizard using the current web-app expense contract. Required fields are title, amount, purchaseDate, categoryId, payerId (the parent who owes), and at least one child user id. Supports one previously-uploaded receipt PDF and private entries. privateExpense=true creates an expense visible only to you; false/default creates the normal shared expense. receiptFileId should come from ofw_upload_expense_pdf. A shared expense is a money claim that appears in the ledger in front of the co-parent immediately, and this server cannot delete it. If the request fails without a definitive answer the result is EXPENSE_UNCONFIRMED: the expense may already exist, so do NOT retry until ofw_list_expenses shows it did not land. Asks the user to confirm first: a confirmation prompt where the client supports one; otherwise the first call performs NO write and returns a preview of exactly what would happen plus a confirmToken, and only a repeat call with that token proceeds (see MCP_CONFIRM_MODE).
| Name | Required | Description | Default |
|---|---|---|---|
| title | Yes | Expense title/name shown in the OFW expense log | |
| amount | Yes | Full expense amount before OFW applies the category split | |
| payerId | Yes | OFW userId of the parent who owes/reimburses this expense | |
| children | Yes | One or more OFW child userIds associated with the expense | |
| categoryId | Yes | OFW expense category id (for example General is commonly id 1; use the id from OFW, not the category display name) | |
| description | No | Optional supporting description/details for the expense | |
| confirmToken | No | ONLY for the two-step confirmation fallback (a client without MCP elicitation). The confirmToken from this same tool's phase-1 "confirmation-required" response, passed back ONLY after the user has seen that preview and explicitly approved it in chat — never on the first call, never invented, never reused. Call again with the same arguments. Ignored when the client supports elicitation. | |
| purchaseDate | Yes | Date the expense was incurred, YYYY-MM-DD | |
| receiptFileId | No | Single OFW My Files fileId to attach as the receipt, normally returned by ofw_upload_expense_pdf | |
| privateExpense | No | true = visible only to you; false/default = shared with co-parent |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already flag destructiveHint=true and openWorldHint=true, and the description adds substantial context beyond them: the expense appears in the co-parent's ledger immediately, this server cannot delete it, the EXPENSE_UNCONFIRMED partial-failure state, and the confirmation-preview-with-confirmToken protocol. This is exactly the behavioral disclosure a mutation tool needs.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Dense and front-loaded, leading with the action and required fields before the safety/confirmation caveats. Every sentence is load-bearing, though the run-on confirmation paragraph is somewhat heavy and could be split for readability.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 10-parameter destructive mutation with no output schema, the description covers required fields, receipt dependency, visibility semantics, irreversible ledger effects, partial-failure handling, and the confirmation protocol. Nothing an agent needs to call it correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so baseline is 3, but the description adds cross-field meaning: payerId is characterized as 'the parent who owes', children requires at least one id, receiptFileId should originate from ofw_upload_expense_pdf, and privateExpense's true-vs-default effect is restated with the shared-expense consequence. More than mere repetition.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Log a new expense in OurFamilyWizard') and scopes it to the 'current web-app expense contract', clearly distinguishing it from siblings like ofw_update_expense and ofw_list_expenses. An agent knows exactly what this tool produces without opening the schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly names required fields, routes receipt attachment through ofw_upload_expense_pdf, and gives a precise when-not-to-retry rule (after EXPENSE_UNCONFIRMED, wait for ofw_list_expenses). It also documents the two-step confirmation flow and its fallback, leaving little to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ofw_create_journal_entryCDestructive
Create a new journal entry in OurFamilyWizard
| Name | Required | Description | Default |
|---|---|---|---|
| body | Yes | Entry text content | |
| title | Yes | Entry title |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=false, destructiveHint=true, and openWorldHint=true, so the safety profile is covered. The description adds nothing beyond that: no note on visibility/audience of the entry, no confirmation that it is an additive write despite the destructiveHint flag, and no auth or side-effect context.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
A single front-loaded sentence with zero filler. It is efficient, though the brevity borders on under-specification for a mutation tool rather than being a model of tightness.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a write tool with no output schema, the description should say what happens on success, whether the entry is published immediately, and any permission requirements. It supplies none of that, leaving the agent with only the tool name and the schema.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Both parameters (title, body) have 100% schema description coverage, so the schema carries the semantics. The description does not add format, length, or content constraints beyond the schema, so the baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Create a new journal entry') plus the product context (OurFamilyWizard), so the action is unambiguous. It stops short of differentiating from the sibling ofw_list_journal_entries or clarifying whether entries are private, shared, or family-scoped.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
No when-to-use guidance, no prerequisites, and no mention of alternatives such as ofw_list_journal_entries or ofw_save_draft. The agent must infer entirely from the name when this tool is appropriate.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ofw_delete_draftADestructive
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.
| Name | Required | Description | Default |
|---|---|---|---|
| force | No | Default false. Delete even if the draft changed on OurFamilyWizard since you read it. The discarded server version is echoed back in the response. | |
| messageId | Yes | Draft message ID to delete | |
| expectedRevision | No | The `revision` you got from ofw_list_drafts/ofw_get_message. Asserts you are deleting THAT version; if the draft changed on OFW since, the delete is refused and the current server body returned. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already flag destructiveHint=true, but the description goes well beyond them: it discloses the pre-delete re-read, the refusal-on-change behavior, that the current server body is returned so nothing is lost, and that the local cache is also purged. This is exactly the kind of context an agent needs before calling a destructive tool.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the core action, then the collision-safety semantics. It is a dense single paragraph with em-dash clauses, but nearly every clause carries distinct information; the parenthetical is load-bearing rather than filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
No output schema exists, and the description compensates by explaining what is returned on both paths (current server body on refusal, discarded version echoed when forcing). For a 3-param destructive tool with full annotation coverage, nothing material is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% and the schema already documents all three params well. The description still adds framing value by presenting expectedRevision and force as an explicit either/or choice with the trade-off stated, rather than restating field definitions.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Delete a draft message from OurFamilyWizard') plus the local-cache side effect, which no sibling tool implies. It is trivially distinguishable from ofw_save_draft, ofw_list_drafts, and ofw_delete_event.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives clear conditional guidance: pass expectedRevision to assert a version, or force:true to delete regardless. It does not explicitly name sibling alternatives (e.g., why not ofw_save_draft over an existing draft), but the when-to-use decision between the two modes is fully spelled out.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ofw_delete_eventADestructive
Delete an OurFamilyWizard calendar event. Reads the event first; deleting one the co-parent can see is confirmed first, with a preview of exactly which event (title, date, time) is removed, and is refused if the event changed on OFW after that preview. If the request fails without a definitive answer the result is EVENT_UNCONFIRMED: the event may already be gone, so do NOT retry until ofw_list_events shows whether it is still there. Asks the user to confirm first: a confirmation prompt where the client supports one; otherwise the first call performs NO write and returns a preview of exactly what would happen plus a confirmToken, and only a repeat call with that token proceeds (see MCP_CONFIRM_MODE).
| Name | Required | Description | Default |
|---|---|---|---|
| eventId | Yes | Event id — the `id` from ofw_list_events / eventRecurrenceId from ofw_create_event | |
| confirmToken | No | ONLY for the two-step confirmation fallback (a client without MCP elicitation). The confirmToken from this same tool's phase-1 "confirmation-required" response, passed back ONLY after the user has seen that preview and explicitly approved it in chat — never on the first call, never invented, never reused. Call again with the same arguments. Ignored when the client supports elicitation. | |
| includeFuture | No | For repeating events: also delete future occurrences (default false) |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already flag destructiveHint=true and openWorldHint=true, but the description adds substantial behavior beyond them: pre-read of the event, confirmation preview with title/date/time, refusal if the event changed after preview, and the EVENT_UNCONFIRMED failure mode with an explicit no-retry instruction. These are exactly the operational traits an agent needs for a destructive tool.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the action and its side effects, and every sentence carries operational content, but the two confirmation paragraphs overlap somewhat (elicitation vs. fallback restates the same preview-then-proceed contract). Slightly longer than strictly necessary, though nothing is wasted in a way that hurts selection.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With annotations covering safety and no output schema, the description fully covers mutation semantics, the confirmation protocol, the failure mode, and retry policy. Nothing an agent needs to call this correctly and safely is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3; the description goes further by explaining the confirmToken lifecycle (first call performs no write, repeat call with the token proceeds) and the includeFuture-relevant confirmation flow. The remaining param detail (eventId source, includeFuture default) lives in the schema, which is acceptable.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb+resource ("Delete an OurFamilyWizard calendar event") and clearly distinguishes this tool from siblings like ofw_list_events, ofw_create_event, and ofw_update_event. An agent can identify exactly what this does without opening any schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives explicit when-to-use rules (after reading the event, after user confirmation), a when-NOT path (do not retry on EVENT_UNCONFIRMED until ofw_list_events confirms state), and names the sibling to consult. This is unusually complete routing guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ofw_download_attachmentADestructive
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. Images and raw bytes are only returned inline up to 10 MiB; a larger file fails with a tool error that says how to get it instead (extracted text is unaffected — it is bounded by maxChars). 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; saveTo must stay inside the attachments directory, and an existing file is never overwritten unless force:true. Re-downloading to the same path is a no-op (disk mode only).
| Name | Required | Description | Default |
|---|---|---|---|
| force | No | Re-download even if already on disk, replacing any existing file at the destination. Default false. Ignored when inline:true (inline always fetches fresh bytes, or reuses an on-disk copy if present). | |
| parts | No | Which sheets / slides / pages to extract, e.g. "1-3,5" (1-based positions) or a sheet name like "2026". A bare number matches either a position or a name. Omit for everything. Unselected parts are listed in `extracted.omitted`. | |
| fileId | Yes | Attachment file id (from ofw_get_message → attachments[].fileId) | |
| inline | No | If true, return content inline as MCP content blocks and skip the disk write. If false, write to disk and return the path — except on a hosted deployment with no filesystem, where inline is forced (forcedInline:true) so the content is still returned. If omitted, falls back to the OFW_INLINE_ATTACHMENTS env var (default: false = disk). | |
| saveTo | No | Path or directory to write to, INSIDE the attachments directory (OFW_ATTACHMENTS_DIR, default ~/Downloads/ofw-mcp); a relative path is resolved against it and anything outside it is refused. If a directory (trailing /), the OFW filename is used. Default: <attachments dir>/<fileId>-<filename>. An existing file is not overwritten unless force:true. Ignored when inline is in effect. | |
| extract | No | Whether to extract readable content from the file. Default: on for inline delivery of any non-image type, off in disk mode. Set false to get the raw bytes inline instead of extracted text (e.g. to hash or re-upload the file); set true in disk mode to get both the saved path and the extracted content. | |
| maxChars | No | Ceiling on extracted characters (default 50000). Over it, content is clipped on a row/line boundary, `truncated` is set, and anything dropped whole is listed in `extracted.omitted`. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Goes well beyond the annotations: it documents the 10 MiB inline ceiling, the tool-error behavior and its remediation, forcedInline on filesystem-less deployments, the no-overwrite-unless-force rule, the disk no-op on re-download, and saveTo path-containment. It also explains the `deliveredVia`/`deliveryAttempts` bookkeeping, which annotations cannot express.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with purpose, then the delivery ladder, then edge cases – a logical order. It is dense and long for a single tool, and some sentences (e.g. the inline/extract default interplay) partially restate the schema, but nearly every sentence carries operational information.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 7-parameter tool with no output schema, the description supplies the response shape (`extracted`, `deliveredVia`, `deliveryAttempts`, `truncated`, `omitted`) plus failure and fallback behavior. An agent has everything needed to call it correctly and interpret the result.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3, but the description adds cross-parameter meaning the schema does not: the OFW_INLINE_ATTACHMENTS default for `inline`, the interaction of `extract` with inline vs disk mode, and the containment rule for `saveTo`. It stops short of restating the enum-like `parts` syntax, which the schema already carries.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The first sentence states a specific verb and resource ('Download an OFW message attachment by fileId') and adds the outcome ('return content you can actually read'). It is trivially distinguishable from the opposite-direction sibling ofw_upload_attachment and from ofw_get_message (which supplies the fileId).
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly tells the agent how to source fileId ('from attachments[].fileId on ofw_get_message') and lays out when each mode applies: inline vs disk, the hosted-deployment fallback, when extract defaults on or off, and when to set force. The alternative paths are named and conditioned rather than left to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ofw_get_expense_totalsBRead-only
Get OurFamilyWizard expense summary totals (owed/paid)
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true and openWorldHint=true, so the safety profile is covered. The description contributes only the '(owed/paid)' characterization of the returned totals; it says nothing about scope (date range, per-child breakdown), freshness, or auth requirements.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
A single front-loaded phrase with no filler, which is appropriately sized for a zero-argument read tool. It is arguably too terse, but nothing is wasted.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no parameters, no output schema, and annotations covering only safety, the description should explain what the totals cover (time window, per-child or per-category breakdown). Because it does not, an agent cannot predict the scope of the returned summary.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool takes zero parameters, so there is nothing for the description to disambiguate and the baseline of 4 applies. No parameter syntax or defaults are needed.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb ('Get') plus resource ('OurFamilyWizard expense summary totals') and narrows it with '(owed/paid)'. It is distinguishable from ofw_list_expenses by the 'summary totals' scope, though it never names that sibling to sharpen the contrast.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
There is no when-to-use or when-not-to-use guidance; the agent must infer from the phrase 'summary totals' that this is an aggregate view rather than the itemized list returned by ofw_list_expenses. No prerequisites, scope, or alternatives are stated.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ofw_get_messageADestructive
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.
| Name | Required | Description | Default |
|---|---|---|---|
| view | No | Response shape: "compact" (default) drops fields the response already carries elsewhere; "full" returns every field this server understands. compact omits OurFamilyWizard's raw `listData` echo, which duplicates this record's own id, subject, sentAt, recipients and read flag; the sender is promoted to `from`, and `files`/`replied` are kept. Pass "full" for the echo. | |
| messageId | Yes | Message ID (also accepts draft IDs — drafts are routed via the drafts cache) | |
| allowMarkRead | No | Default true (the long-standing behaviour). Set false to refuse a fetch that would mark an unread INBOX message as READ on OurFamilyWizard — an irreversible, co-parent-visible change to the record. Reads that cannot stamp anything (a cached body, a sent message, an already-read message) still succeed. The server-wide OFW_ALLOW_MARK_READ=false is a ceiling this argument cannot raise. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The annotations (readOnlyHint=false, destructiveHint=true) only flag that something mutating can happen; the description supplies the crucial specifics — that fetching an unread inbox message marks it read, stamps a co-parent-visible 'First Viewed' time, and that this is irreversible and cannot be undone. It also discloses cache-vs-fetch behavior and the OFW_ALLOW_MARK_READ=false ceiling, well beyond structured data.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the core purpose, then the destructive mark-read warning, then draft nuances — a sensible priority order. It is dense but nearly every sentence carries information; there is minor redundancy with the schema on allowMarkRead that keeps it from a 5.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description carries the return-shape burden and does so: it explains folder values ('inbox'/'sent'/'drafts'), the absence of fromUser on drafts, how sentAt/fetchedBodyAt mirror modifiedAt, and the compact/full field differences. An agent has everything needed to call and interpret the result.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the schema already documents view, messageId, and allowMarkRead thoroughly. The description largely restates the allowMarkRead semantics and draft-ID routing rather than adding new parameter-level detail, so baseline 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a precise verb+resource ('Get a single OurFamilyWizard message OR draft by ID') and immediately distinguishes itself from the sibling list tools (ofw_list_messages, ofw_list_drafts) by scope. An agent can tell exactly what this retrieves without opening the schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives clear conditional guidance: use allowMarkRead:false to refuse a fetch that would mark an unread inbox message read, and notes that cached/sent/already-read reads are unaffected. It also explains the drafts-routing path. It stops short of explicitly naming which sibling to call instead for listing or syncing, so it's strong context rather than a full when/when-not/alternatives map.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ofw_get_notificationsADestructive
Get OurFamilyWizard dashboard summary: unread message count, upcoming events, outstanding expenses. Note: updates your last-seen status.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description discloses the non-obvious side effect — 'updates your last-seen status' — which explains the destructiveHint=true / readOnlyHint=false annotations rather than contradicting them. It stops short of stating downstream consequences (e.g., whether unread counts change on subsequent calls) or any auth requirements.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two sentences, zero filler: the payload of the summary comes first and the side-effect caveat is a clearly marked trailing note. Nothing needs to be trimmed or moved.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no parameters, no output schema, and only three annotations, this simple tool is fully covered: the description states what is returned and the one behavior the annotations flag. An agent has everything needed to decide to call it and what to expect.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool takes no parameters, so there is nothing for the description to disambiguate; the 4 baseline applies. The description correctly avoids inventing parameters or inlining an argument shape.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Specific verb ('Get') plus a named resource ('OurFamilyWizard dashboard summary') with the three aggregates it returns spelled out (unread messages, upcoming events, outstanding expenses). That enumeration distinguishes it from sibling list tools such as ofw_list_messages, ofw_list_events, and ofw_get_expense_totals without needing to open any schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The 'dashboard summary' framing implies the use case (a quick overview rather than a full listing), but there is no explicit when-to-use statement and no alternatives named. The agent must infer that this is the aggregate shortcut versus the individual list tools.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ofw_get_profileBRead-only
Get current user and co-parent profile information from OurFamilyWizard
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true and openWorldHint=true, so the safety profile is covered. The description adds essentially nothing beyond the annotations—no detail on what 'profile information' includes, whether it requires an active session, or failure modes with no parameters.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
A single concise sentence with no filler, front-loaded with the action. It is efficient, though it could have afforded one more clause to clarify the returned data.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a zero-parameter read-only tool with annotations and no output schema, the description is minimally adequate. It doesn't explain the return shape or co-parent edge cases, which would matter for an agent consuming the response.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool takes zero parameters, so the baseline for parameter semantics is 4 per the rubric. The description correctly implies no input is needed to fetch the current profile, and the empty schema is self-consistent.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a verb and resource ('get...profile information') from a named system, which is clearer than a tautology. But 'current user and co-parent profile information' is vague about what fields are returned, and it doesn't differentiate why to pick this over the sibling listing/retrieval tools.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
There is no guidance on when to use this tool versus alternatives, no prerequisites, and no mention of the lack of parameters or what happens if the user has no co-parent linked. The agent must infer usage entirely.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ofw_get_unread_sentARead-only
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.
| Name | Required | Description | Default |
|---|---|---|---|
| page | No | Page (default 1) | |
| size | No | Per page (default 50) | |
| autoRefresh | No | If the result comes back EMPTY from a cache that is not verified-fresh, sync the backing folders first and answer from the refreshed cache instead of refusing. Defaults to the OFW_AUTO_REFRESH env var (false unless set), in which case the call refuses with result:"UNVERIFIED_EMPTY" and names the remedy. Costs OFW requests when it fires. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Goes well past the readOnlyHint/openWorldHint annotations: it discloses that reads come from a local cache, that an unverified-fresh empty SENT cache is REFUSED with result:"UNVERIFIED_EMPTY" rather than reported as empty, and that autoRefresh costs OFW requests when it fires. This is exactly the behavioral context annotations cannot convey.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three sentences, no filler, and the most decision-relevant fact (cache-sourced unread sent mail) leads. The refusal/autoRefresh caveat is compact and specific rather than hedged.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description still tells the agent what comes back (`complete`), and it fully covers the failure mode and its remedy. For a zero-required-param read tool, nothing needed to call it correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so page/size/autoRefresh semantics (including the OFW_AUTO_REFRESH default) are already documented in the schema. The description reinforces autoRefresh's role and its cost but adds no syntax or meaning beyond what the schema states; baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource with a precise scope qualifier: 'sent messages that have not been read by one or more recipients.' That scope alone separates it from ofw_list_messages (all messages) and ofw_get_message (single message), so an agent can route without opening a schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives a concrete conditional remedy: pass autoRefresh:true to sync and answer instead of receiving a refusal, and names the refusal result value. It does not compare against alternatives such as ofw_sync_messages or ofw_check_freshness, which is the only gap.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ofw_healthcheckVerify credentials and upstream reachabilityARead-onlyIdempotent
Resolves the credential the way real tools do, then makes one authenticated request to ourfamilywizard.com. Reports which source supplied the credential, whether ourfamilywizard.com accepted it, the round-trip time, and a plain-English hint distinguishing 'no credential' from 'credential rejected' from 'a ourfamilywizard.com-side problem'. Read-only; never returns the credential itself. Call this when a real tool fails and you want to know which hop broke.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint, openWorldHint, and idempotentHint. The description adds valuable behavioral context: it explicitly states 'Read-only; never returns the credential itself' and describes the exact outputs (source, acceptance, round-trip time, and a plain-English hint distinguishing failure modes). It also mentions that it makes one authenticated request, which is a side effect not covered by annotations. No contradiction with annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is approximately 70 words and front-loads the core action ('Resolves the credential... makes one authenticated request') before listing outputs and usage. Every sentence adds value: the credential resolution, the request, the reported metrics, the read-only/never-returns-credential note, and the when-to-call guidance. There is no redundancy.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a zero-parameter diagnostic tool with no output schema, the description adequately explains what it does, what it reports, and when to use it. It specifies the three failure categories and mentions round-trip time, giving the agent a clear expectation of the result. No critical information is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool has zero parameters, and the schema coverage is 100% (vacuously). The description does not need to explain parameters because there are none. The baseline for 0 params is 4, and no additional parameter semantics are required or provided.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool's purpose: resolving the credential as real tools do and making an authenticated request to test upstream reachability. It explicitly distinguishes this healthcheck from the sibling tools (messages, expenses, etc.) by naming it as a diagnostic that reports credential source, acceptance, and round-trip time. The verb 'resolves' and 'makes' are specific, and the resource is named.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides explicit guidance on when to call: 'Call this when a real tool fails and you want to know which hop broke.' This gives a clear trigger condition and implies it's for troubleshooting, not routine use. It does not mention alternatives, but the context is sufficient because this tool is unique among siblings for diagnostics.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ofw_list_draftsA
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.
| Name | Required | Description | Default |
|---|---|---|---|
| page | No | Page number (default 1) | |
| size | No | Drafts per page (default 50) | |
| view | No | Response shape: "compact" (default) drops fields the response already carries elsewhere; "full" returns every field this server understands. compact omits OurFamilyWizard's raw `listData` echo, which duplicates this draft's own id, subject, modifiedAt and recipients. `revision`, `draftKey` and `cacheStatus` are kept on both rungs. | |
| verify | No | Default true: when the drafts cache is not verified-fresh, run a drafts sync first (cheap — one list page plus one detail per draft) so the response is server-confirmed in one call. Set false to serve straight from the local cache with no OFW requests. | |
| autoRefresh | No | If the result comes back EMPTY from a cache that is not verified-fresh, sync the backing folders first and answer from the refreshed cache instead of refusing. Defaults to the OFW_AUTO_REFRESH env var (false unless set), in which case the call refuses with result:"UNVERIFIED_EMPTY" and names the remedy. Costs OFW requests when it fires. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Goes well beyond the annotations by disclosing side effects (a sync of one list page plus one detail per draft runs first), cost implications ('Costs OFW requests when it fires'), a refusal path with a specific result code, and the stability guarantee of draftKey. This is exactly the behavioral context an agent needs before calling.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loads the single most important fact (one-call verification) and stays on topic throughout. It is dense and slightly repetitive ('ONE call' / 'without a second call' / 'in one call'), which costs a bit of tightness.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description correctly carries the return semantics: the `complete` boolean over the result set, the `draftKey` field, and the `result:"UNVERIFIED_EMPTY"` case. It omits other error/edge results and any note on pagination interaction with `complete`, which is a minor gap for a 5-param list tool.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the per-parameter docs already carry the baseline. The description still adds value by explaining the interaction between verify and autoRefresh and tying them to the refusal behavior, which the isolated schema entries do not connect.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb+resource ('List draft messages') and immediately scopes it ('verified against OurFamilyWizard in ONE call'). It is clearly separable from the sibling write tools ofw_save_draft and ofw_delete_draft, and the verification framing distinguishes it from a plain cached list.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives explicit conditions: verify:true (default) for server-confirmed answers, verify:false for pure cache reads, and autoRefresh:true to escape the UNVERIFIED_EMPTY refusal. It does not name adjacent siblings (ofw_check_freshness, ofw_sync_messages) as alternatives, so the routing is internal to the tool rather than cross-tool.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ofw_list_eventsARead-only
List OurFamilyWizard calendar events in a date range
| Name | Required | Description | Default |
|---|---|---|---|
| endDate | Yes | End date YYYY-MM-DD | |
| detailed | No | Return full event details (default false) | |
| startDate | Yes | Start date YYYY-MM-DD |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true and openWorldHint=true, so the safety profile is covered. The description adds the date-range scoping constraint, but says nothing about pagination, result caps, or how the default detailed=false affects the payload, leaving real behavioral gaps for a list operation.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
A single front-loaded sentence with no filler; the scope constraint (date range) immediately follows the verb and resource. Nothing is wasted or buried.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a simple three-parameter read tool with a fully documented schema and no output schema, the description covers what the tool returns conceptually (calendar events). Only pagination/result-limit behavior is unaddressed, a minor gap given the tool's simplicity.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100% (startDate, endDate, detailed all documented with format and default), so the schema carries parameter semantics. The description only restates the date-range concept and adds nothing about the detailed flag, which is the correct baseline of 3.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb (List), resource (OurFamilyWizard calendar events), and scope (in a date range), which cleanly separates it from ofw_create_event, ofw_update_event, and ofw_delete_event. It does not name those siblings explicitly, but the read/list framing is unambiguous.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Usage is only implied: the agent can infer 'call this to enumerate events over a period,' and the date-range phrase hints at the required startDate/endDate. There is no explicit when-to-use, when-not-to-use, or alternative named, so it lands at minimum-viable.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ofw_list_expense_categoriesARead-only
List OurFamilyWizard expense categories, including preset and custom categories with their responsibility split metadata. Read-only.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true and openWorldHint=true, so the 'Read-only' sentence largely restates structured data. What the description does add beyond the annotations is the composition of the result set (preset plus custom categories, each carrying responsibility-split metadata), which is genuinely useful context, but nothing about ordering, volume, or freshness is disclosed.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two short sentences, front-loaded with the verb and resource, with no filler. The trailing 'Read-only' sentence is minor redundancy against the annotations, which keeps it just short of ideal.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no parameters, no output schema, and annotations covering the safety profile, the description's job is narrow. It adequately conveys what the tool lists and what the entries contain; only return-shape specifics like ordering or pagination are absent, and those are minor here.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool takes zero parameters and schema description coverage is 100%, so the baseline of 4 applies. There are no arguments whose semantics the description could clarify or omit.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb ('List') and a precise resource ('OurFamilyWizard expense categories'), and the resource is clearly distinct from siblings like ofw_list_expenses, ofw_get_expense_totals, and ofw_create_expense. The added detail that the result includes preset and custom categories with responsibility-split metadata further pins down what is returned.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description never says when to reach for this tool versus ofw_list_expenses or ofw_get_expense_totals, nor does it state prerequisites or typical workflows (e.g., call this before creating an expense to obtain a valid category). 'Read-only' is a safety trait, not usage guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ofw_list_expensesARead-only
List OurFamilyWizard expenses. OFW pages this endpoint with 1-based page/size parameters; its older start/max parameters are ignored and repeatedly return page 1. The response leads with hasMore and nextPage (null when exhausted) before the records. Continue by passing nextPage.
| Name | Required | Description | Default |
|---|---|---|---|
| page | No | 1-based page number (default 1). To continue, pass the nextPage returned by the previous response. | |
| size | No | Requested page size (default 20). OFW may cap or normalize this value. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true and openWorldHint=true, setting a lower bar. The description adds valuable non-obvious behavior: pagination quirks (old start/max ignored, always page 1) and response structure leading with hasMore and nextPage. It doesn't cover auth or rate limits, but for a read operation, this is strong added context.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three sentences, each earning its place: purpose, pagination caveat, and continuation instruction. Front-loaded with what the tool does. Slightly dense with technical details, but efficient and no wasted words.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With annotations covering read-only and open-world behavior, and no output schema, the description provides the critical missing piece: pagination mechanics and continuation. It doesn't describe return fields beyond nextPage/hasMore, but that's reasonable given no output schema and the focus on correct invocation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so baseline is 3. The description adds meaning beyond the schema by explaining that the older start/max parameters are ignored and that continuation should use nextPage from the response. This directly informs parameter usage beyond the schema's formal definitions.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a clear verb+resource: 'List OurFamilyWizard expenses.' An agent can distinguish it from siblings like ofw_get_expense_totals (totals vs. list) and ofw_create_expense (create vs. list). No explicit sibling naming, but the verb+resource is specific enough to differentiate from most.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description implies when to use pagination by explaining nextPage continuation, which is useful guidance. However, it doesn't explicitly state when to use this tool vs. alternatives like ofw_get_expense_totals or mention any exclusions. Usage is implied rather than explicit.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ofw_list_journal_entriesARead-only
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.
| Name | Required | Description | Default |
|---|---|---|---|
| max | No | Max results (default 10) | |
| start | No | Start offset, 1-based (default 1). To continue a listing, pass the `nextStart` from the previous response. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no output schema, the description carries the burden of return-shape disclosure and does so richly: it explains that the response leads with `hasMore` and `nextStart`, that `nextStart` is null when exhausted, and that a truncated response still signals whether more remain. This is substantial behavioral context beyond what the readOnlyHint/openWorldHint annotations provide.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The purpose is front-loaded, followed by paging mechanics and then the caution about not asserting counts from one page. Three sentences, each carrying distinct information, though the paging-state exposition is somewhat dense for a two-parameter list tool.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a paged list tool with no output schema, the description covers the critical unknowns: pagination mechanism and the return envelope (hasMore/nextStart). It stops short of describing the journal entry fields themselves, so it is strong but not exhaustive.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the schema already documents `start` (1-based, continue via `nextStart`) and `max` (default 10). The description only restates that paging is offset-based and 1-based, adding no semantics beyond the schema, so the baseline of 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource: 'List OurFamilyWizard journal entries.' This clearly separates it from the write-side sibling ofw_create_journal_entry and from other list tools (messages, events, expenses) by its resource noun. It doesn't explicitly name an alternative tool, which keeps it off a 5.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description tells the agent how to continue paging and warns against stating counts or absence from a single page, which is genuine usage guidance about interpreting results. However, it never says when to choose this list tool over related ones or states prerequisites, so the routing guidance is only implied by the verb/resource.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ofw_list_message_foldersARead-only
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.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint and openWorldHint, so the safety profile is covered. The description adds genuine behavioral context beyond that: data is fetched LIVE so counts are current, and it explicitly bounds scope with 'Does NOT return message content.' That freshness and boundary detail is exactly what annotations cannot convey.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three tight sentences, zero filler, and the core purpose plus unread-count payload is front-loaded. The workflow link and scope boundary follow in priority order.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a zero-parameter read-only list tool this is complete: it describes what is returned (folders, unread counts, folder IDs), how fresh the data is, and what is excluded. The absence of an output schema is fully compensated by the description's return-value summary.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool takes zero parameters, so there is nothing for the description to disambiguate. Baseline of 4 applies; no parameter semantics are needed or missing.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource (list OFW message folders), scopes it with examples (inbox, sent), and adds the differentiating payload (unread counts). An agent can distinguish it from ofw_list_messages, which lists messages rather than folders.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly links this tool into a workflow by stating it returns the folder IDs needed to call ofw_list_messages, so the agent knows why and when to invoke it. It does not name an exclusion or a when-not-to-use case, which keeps it short of a 5.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ofw_list_messagesARead-only
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.
| Name | Required | Description | Default |
|---|---|---|---|
| q | No | Substring match on subject AND body (case-insensitive). Use to find messages on a specific topic. | |
| page | No | Page number (default 1) | |
| size | No | Messages per page (default 50) | |
| sort | No | Result order: "newest" (default, newest first) or "oldest" (oldest first). This decides which end a truncated page keeps — with "newest" page 1 of a wide date range holds its most RECENT slice, with "oldest" its earliest. Use "oldest" to start at the old end of a range instead of paging to it. | |
| view | No | Response shape: "compact" (default) drops fields the response already carries elsewhere; "full" returns every field this server understands. compact omits OurFamilyWizard's raw `listData` echo, which duplicates this record's own id, subject, sentAt, recipients and read flag; the sender is promoted to `from`, and `files`/`replied` are kept. Pass "full" for the echo. | |
| since | No | ISO date or datetime — only messages with sent_at >= since (inclusive). A value with an offset or Z is compared as that instant; a naive value is read as the account's local time (DISPLAY_TZ) | |
| until | No | ISO date or datetime — only messages with sent_at < until (exclusive). A value with an offset or Z is compared as that instant; a naive value is read as the account's local time (DISPLAY_TZ) | |
| folderId | No | Folder name: "inbox", "sent", or "both" (default "both") | |
| autoRefresh | No | If the result comes back EMPTY from a cache that is not verified-fresh, sync the backing folders first and answer from the refreshed cache instead of refusing. Defaults to the OFW_AUTO_REFRESH env var (false unless set), in which case the call refuses with result:"UNVERIFIED_EMPTY" and names the remedy. Costs OFW requests when it fires. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Goes well beyond the readOnly/openWorld annotations: it explains the `complete` boolean as a result-set guarantee tied to freshness.asOf, discloses the UNVERIFIED_EMPTY refusal for stale-cache empty results, notes that autoRefresh costs OFW requests, and describes how sort decides which end a truncated page keeps. These are exactly the behavioral facts an agent cannot infer from annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with purpose and dense with useful detail, but long; the sort sentence partially restates the schema's sort description and the final autoRefresh sentence is a heavy compound. Nearly every clause earns its place, with minor redundancy.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 9-parameter, no-output-schema read tool, this covers the crucial contract: what `complete` means, when results are refused rather than reported empty, and how to recover. Nothing an agent needs to call it correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is already 100% with rich per-parameter text, so the baseline is 3. The description adds strategy-level meaning (filters over paging, 1-based page, sort's effect on truncation, autoRefresh as the remedy for refusals) that a per-parameter schema alone does not convey.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('List messages') and immediately scopes it ('from the local OurFamilyWizard cache'), which separates it from the direct-fetch sibling ofw_get_message and from ofw_get_unread_sent. The description of filtering, pagination, and the result-set contract makes the aggregate-listing role unambiguous.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives real operational guidance: prefer filters over walking pages because the cache may hold 1000+ messages, and use sort:"oldest" to start at the old end of a range. It never explicitly names an alternative sibling tool or states a when-not-to-use condition, so it falls short of full routing guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ofw_save_draftADestructive
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.
| Name | Required | Description | Default |
|---|---|---|---|
| body | Yes | Message body text | |
| force | No | Default false. Overwrite even when the draft changed on OurFamilyWizard since you read it. The discarded server version is echoed back in the response. Only use after showing the user the conflict. | |
| subject | Yes | Message subject | |
| messageId | No | ID of an existing draft to replace (the new draft will have a new id; the old is deleted) | |
| myFileIDs | No | Attachment file ids (from ofw_upload_attachment) | |
| replyToId | No | ID of the message this draft replies to | |
| recipientIds | No | Array of recipient user IDs (optional for drafts) | |
| expectedRevision | No | With messageId: the `revision` you got from ofw_list_drafts/ofw_get_message for that draft. Asserts you are replacing THAT version. If the draft changed on OFW since, the write is refused and the current server body is returned. Omit and the tool compares the server against the local cache instead — omitting never means "overwrite anyway". |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Far exceeds what the annotations provide (destructiveHint/openWorldHint/idempotentHint only). It discloses that recipients are not persisted, that replacement destroys the old draft and produces a new id, that the update-in-place endpoint is never used, that replyToId may be normalized, and that writes can be refused with serverBody returned. This is exactly the behavioral context an agent needs.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the action, then organized under RECIPIENTS/IDENTITY/THREADING/SAFETY headings, so it is navigable. It is still dense and occasionally over-explains (e.g. the parenthetical about what is 'noted once in the response, not warned about'), but nearly every sentence carries actionable constraint information.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Covers identity, replacement/conflict handling, threading, attachments, and safety for an 8-parameter destructive mutation tool with no output schema. Nothing an agent needs to call it correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is already 100%, but the description adds real meaning beyond the schema fields: recipientIds are accepted-but-dropped, myFileIDs come from ofw_upload_attachment, messageId triggers a re-read and conflict check, and omitting expectedRevision does not mean 'overwrite anyway'.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb+resource ('Save a message as a draft in OurFamilyWizard') and immediately scopes it against siblings like ofw_send_message and ofw_delete_draft by clarifying draft semantics. An agent can identify this tool without opening any schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives explicit routing rules: use recipientIds at send time rather than on drafts, pass messageId to replace, pass expectedRevision when replacing a known version. It also states when NOT to overwrite and how to recover (merge into serverBody and retry).
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ofw_send_messageADestructive
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. If the send request times out or drops without a definitive answer, the result is SEND_UNCONFIRMED: the message may already have been delivered, so do NOT retry until a sent-folder sync (or ourfamilywizard.com) shows it did not go out. 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. Asks the user to confirm first: a confirmation prompt where the client supports one; otherwise the first call performs NO write and returns a preview of exactly what would happen plus a confirmToken, and only a repeat call with that token proceeds (see MCP_CONFIRM_MODE).
| Name | Required | Description | Default |
|---|---|---|---|
| body | No | Message body text. Required unless draftId/messageId is given (then it overrides the server draft's body — omit it to send exactly what is on OurFamilyWizard). | |
| force | No | Default false. Send even when the draft changed on OurFamilyWizard since you read it, or its current state could not be read. Only use after showing the user the conflict. | |
| draftId | No | ID of an existing draft to send. The draft is re-read from OurFamilyWizard and its SERVER content is sent; missing subject/body default from it. Guarded: a draft that changed since you read it, or that was already sent/deleted, refuses rather than sending blind. | |
| subject | No | Message subject. Required unless draftId/messageId is given (then it overrides the server draft's subject). | |
| messageId | No | Synonym for draftId (if both are passed they must be equal). | |
| myFileIDs | No | Attachment file ids (from ofw_upload_attachment) to attach to the message. When sending by draftId, omit it to carry the server draft's own attachments over; passing it overrides them. | |
| replyToId | No | ID of the message being replied to. Defaults to the draft's stored reply target when sending by draftId. | |
| confirmToken | No | ONLY for the two-step confirmation fallback (a client without MCP elicitation). The confirmToken from this same tool's phase-1 "confirmation-required" response, passed back ONLY after the user has seen that preview and explicitly approved it in chat — never on the first call, never invented, never reused. Call again with the same arguments. Ignored when the client supports elicitation. | |
| recipientIds | No | Array of recipient user IDs (get from ofw_get_profile). Usually required even when sending a draft: OurFamilyWizard does not persist recipients on drafts. | |
| expectedRevision | No | With draftId: the `revision` from ofw_list_drafts / ofw_get_message / ofw_check_freshness for that draft. Asserts you are sending THAT version; if the draft changed on OFW since, the send is refused and the current server content returned. Omit and the tool compares the server against the local cache instead — omitting never means "send whatever is there now". | |
| deleteDraftOnSuccess | No | Default true. Delete the source draft after — and ONLY after — the send is confirmed (new message id returned and the re-fetched sent record checks out). Set false to keep the draft. On a failed or unverifiable send the draft is ALWAYS kept, regardless of this flag. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already flag destructiveHint=true and openWorldHint=true, but the description goes well beyond: server-side re-read semantics, expectedRevision guard with refusal behavior, automatic draft deletion only on confirmed send, draftRetained on failure, and the SEND_UNCONFIRMED ambiguity state. This is exactly the extra context needed for an irreversible operation.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loads the key fact (irreversible) and then walks the scenarios in a logical order, but it is very long and re-states several details already carried verbatim in the schema descriptions (myFileIDs override, deleteDraftOnSuccess, expectedRevision). The all-caps emphasis aids scanning but the density costs it.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
There is no output schema, so the description correctly carries the return contract itself: sentMessageId, draftKey, threaded, draftDeleted, draftRetained with reason, and the SEND_UNCONFIRMED outcome. Nothing an agent needs to call or interpret this 11-parameter tool is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% so the baseline is 3; the description still adds cross-parameter workflow meaning — that recipientIds is usually required even for drafts (OFW does not persist recipients), how expectedRevision interacts with omission, and when force is permissible. Some of this duplicates the schema's own per-field text, so it is not a full 5.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb+resource ('Send a message via OurFamilyWizard') and immediately frames it as the one irreversible operation, which distinguishes it from the reversible siblings ofw_save_draft and ofw_delete_draft. An agent can tell it apart from every sibling without opening the schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives explicit routing: send-existing-draft via draftId as the safe default vs compose-from-scratch with subject/body/recipientIds, plus when not to retry (SEND_UNCONFIRMED until a sent-folder sync confirms). It also names the confirmation prerequisite and the confirmToken fallback path.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ofw_statusADestructive
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.
| Name | Required | Description | Default |
|---|---|---|---|
| ids | No | Message/draft ids to resolve to a live state (combined with draftKeys, max 25 probes per call). | |
| draftKeys | No | Stable draft keys (from ofw_save_draft / ofw_list_drafts) to resolve to their CURRENT id and state. | |
| allowMarkRead | No | Default false. An id whose cached state cannot rule out an unread INBOX message can only be probed by fetching its detail, which marks it READ on OurFamilyWizard — irreversible and co-parent-visible. Those are skipped unless this is true. Cached drafts, sent messages and already-read messages are always probed. Capped by OFW_ALLOW_MARK_READ. | |
| includeDraftInventory | No | Return the full current draft list, verified against OurFamilyWizard first. Defaults to TRUE when neither ids nor draftKeys is given (so a bare ofw_status() is a complete status snapshot), otherwise false. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Adds real context beyond annotations: the top-level `complete` flag is true ONLY when everything was verified live, with an explicit instruction not to state draft counts or lifecycle claims when false. It also discloses the create-then-delete id churn and that a draftKey keeps resolving after the draft is sent. It does not surface the irreversible/co-parent-visible mark-read side effect in prose (that lives only in the schema), which is the one gap.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The core claim is front-loaded in the first sentence and the remaining sentences each carry distinct information (state enum, complete flag, draftKey identity). It is dense and runs long, with a couple of colloquial asides ('what happened to the thing I was working on?') that could be trimmed.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description carries the return-value burden and does so: it enumerates the `state` enum, `sentAt`/`viewedAt`, `sentMessageId`, and the `complete` verification flag. Combined with a 100%-documented 4-param schema, an agent has everything needed to call and interpret it correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3, but the description goes further by explaining what a draftKey actually is — a stable identity that resolves to the chain's current id and survives sends — and how ids/draftKeys combine. It stays silent on allowMarkRead, so it does not fully pre-empt the schema's most consequential parameter.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb+resource: a single live call that resolves current draft/message lifecycle state ('where does everything stand?'), returning a full draft inventory verified against OurFamilyWizard. It explicitly carves itself out from session memory and cached reads, so an agent can distinguish it from list/check siblings.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives explicit when-to-use ('should back any status summary about drafts or specific messages') and when-not ('never session memory, and never a cached read alone'). It also explains the two calling modes — bare call for full inventory vs. passing ids/draftKeys for targeted probes.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ofw_sync_messagesADestructive
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).
| Name | Required | Description | Default |
|---|---|---|---|
| deep | No | If true, walk every OFW page until empty regardless of cache state. Use to backfill gaps. Default false. | |
| folders | No | Folders to sync (default: all three). Must be non-empty if given — an empty list would sync nothing while reporting success. | |
| maxRequests | No | Maximum OFW requests this single call may make before pausing. When hit, the response reports done:false — call again with the same arguments to continue. Omit to use the server default (OFW_SYNC_MAX_REQUESTS, or unbounded on local installs). | |
| fetchUnreadBodies | No | If true, also fetch bodies for unread inbox messages — which marks each one READ on OurFamilyWizard and stamps a co-parent-visible "First Viewed" time that cannot be undone. Defaults to the OFW_FETCH_UNREAD_BODIES env var (false unless set), and is forced off entirely when OFW_ALLOW_MARK_READ=false. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Adds substantial behavior beyond the annotations: the mark-as-read side effect on OFW and the irreversible co-parent-visible 'First Viewed' stamp, the per-call request budget and env override, the newest-page-first re-check ordering, and the hosted-vs-local boundedness difference. This is exactly the kind of irreversible-consequence disclosure the destructiveHint=true annotation only hints at.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with purpose and return values, then budget/resume semantics, then local-install behavior — a sensible ordering. It is dense and long for a single paragraph, with some repetition around done:false, but nearly every sentence carries distinct operational information.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
No output schema exists, yet the description describes the response (per-folder counts, list of unfetched unread messages, done flag plus note) and covers resumability, budget sources, side effects, and environment-dependent defaults. An agent has everything needed to call it correctly and to interpret a partial result.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is already 100%, so the schema carries the parameter definitions; the description adds operational meaning on top, notably that maxRequests exhaustion produces done:false and requires a resume call with identical arguments, and that re-checking the newest page happens before deep backfill spends budget. It adds less for folders beyond confirming default coverage.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb+resource with scope: 'Sync messages from OurFamilyWizard into the local cache', and immediately defines the return shape (counts per folder plus unread inbox messages whose bodies were not fetched). This clearly separates it from read-only siblings like ofw_list_messages or ofw_get_message, which the description explicitly routes to for reading bodies.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives explicit when-to-use rules for each behavior: deep:true only to backfill suspected gaps, fetchUnreadBodies only when mark-as-read is acceptable, and 'call again with the SAME arguments to resume' when done:false. It names the alternative tool (ofw_get_message) for the deferred-body case and clarifies that done:false does not mean recent messages are missing.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ofw_update_eventADestructive
Update an existing OurFamilyWizard calendar event. Fetches the event, applies the given changes, and writes the merged result back (OFW has no partial update). A change to an event the co-parent can see (shared before or after the change) is confirmed first; the confirmation is bound to the event exactly as read, so if it changes on OFW in between (say the co-parent edited it) the update is refused instead of overwriting their edit. If the write fails without a definitive answer, or lands but cannot be re-read to confirm it, the result is EVENT_UNCONFIRMED: the change may already be on OFW, so do NOT retry until ofw_list_events shows whether it landed. Asks the user to confirm first: a confirmation prompt where the client supports one; otherwise the first call performs NO write and returns a preview of exactly what would happen plus a confirmToken, and only a repeat call with that token proceeds (see MCP_CONFIRM_MODE).
| Name | Required | Description | Default |
|---|---|---|---|
| notes | No | ||
| title | No | ||
| allDay | No | ||
| endDate | No | End date YYYY-MM-DD (default: startDate) | |
| endTime | No | End time HH:mm, 24-hour (required unless allDay) | |
| eventId | Yes | Event id — the `id` from ofw_list_events / eventRecurrenceId from ofw_create_event | |
| children | No | Child userIds to tag; pass [] to remove all child tags (omit to keep current tags) | |
| location | No | ||
| startDate | No | Start date YYYY-MM-DD | |
| startTime | No | Start time HH:mm, 24-hour (required unless allDay) | |
| confirmToken | No | ONLY for the two-step confirmation fallback (a client without MCP elicitation). The confirmToken from this same tool's phase-1 "confirmation-required" response, passed back ONLY after the user has seen that preview and explicitly approved it in chat — never on the first call, never invented, never reused. Call again with the same arguments. Ignored when the client supports elicitation. | |
| privateEvent | No | true = visible only to you; default false = shared with co-parent | |
| eventParentId | No | userId of the parent the event is 'for' | |
| pickUpParentId | No | userId of the pick-up parent | |
| dropOffParentId | No | userId of the drop-off parent | |
| reminderMinutes | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Goes well beyond the annotations (destructiveHint/openWorldHint): it discloses read-modify-write merging, optimistic-concurrency refusal when the event changed on OFW, the EVENT_UNCONFIRMED failure mode with an explicit do-not-retry instruction, and the elicitation vs confirmToken confirmation paths. This is exactly the behavioral context an agent needs for a destructive mutation.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with purpose, then the concurrency and confirmation behavior in rough priority order. It is dense but nearly every sentence carries load-bearing information; the final confirmation sentence is somewhat jargon-heavy (MCP_CONFIRM_MODE) for its payoff.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 16-param destructive mutation with no output schema, the description covers the safety-critical surface: merge semantics, stale-write refusal, unconfirmed-write handling, and the confirmation protocol. Only the shape of a successful response is left implicit, which the absence of an output schema makes acceptable.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 69%, so the schema carries most parameter documentation. The description adds one meaningful general rule ('writes the merged result back', i.e. omitted fields are preserved) but does not clarify the undocumented params (title, notes, allDay, location), so it neither fully compensates nor is empty.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Update an existing OurFamilyWizard calendar event') and immediately clarifies the implementation posture (fetch, merge, write back, no partial update). This clearly separates it from ofw_create_event / ofw_delete_event / ofw_list_events.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives strong operational context: it directs the agent to ofw_list_events to determine whether an EVENT_UNCONFIRMED write landed, and explains the confirmation requirement before any shared-event change. It never states explicit exclusions against sibling tools, so it stops short of a 5.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ofw_update_expenseADestructive
Change an existing OurFamilyWizard expense. Pass expenseId plus ONLY the fields you want to change; every field you omit keeps its current value. The tool reads the expense from OFW first and sends the merged result, because OFW's update replaces the whole expense and a field left out would otherwise be erased. If a field you did not pass cannot be read back from OFW, the update is refused as EXPENSE_FIELDS_UNREADABLE and names the field; pass it explicitly (description:null or receiptFileId:null to send none). receiptFileId REPLACES all current receipts; omit it to keep them. Set privateExpense=false to publish a private expense to the co-parent. The confirmation is bound to the expense exactly as read, so if it changes on OFW in between, the update is refused instead of overwriting that change. If the request fails without a definitive answer the result is EXPENSE_UNCONFIRMED: the update may already have been applied, so check ofw_list_expenses before retrying. Asks the user to confirm first: a confirmation prompt where the client supports one; otherwise the first call performs NO write and returns a preview of exactly what would happen plus a confirmToken, and only a repeat call with that token proceeds (see MCP_CONFIRM_MODE).
| Name | Required | Description | Default |
|---|---|---|---|
| title | No | New title (omit to keep) | |
| amount | No | New full expense amount (omit to keep) | |
| payerId | No | New payer: the userId of the parent who owes (omit to keep) | |
| children | No | New child userIds, replacing the current list (omit to keep) | |
| expenseId | Yes | Existing OFW expense entity id | |
| categoryId | No | New OFW expense category id (omit to keep) | |
| description | No | New description; null removes it (omit to keep) | |
| confirmToken | No | ONLY for the two-step confirmation fallback (a client without MCP elicitation). The confirmToken from this same tool's phase-1 "confirmation-required" response, passed back ONLY after the user has seen that preview and explicitly approved it in chat — never on the first call, never invented, never reused. Call again with the same arguments. Ignored when the client supports elicitation. | |
| purchaseDate | No | New expense date, YYYY-MM-DD (omit to keep) | |
| receiptFileId | No | Receipt fileId that REPLACES all current receipts; null removes them (omit to keep) | |
| privateExpense | No | true = visible only to you; false = shared with the co-parent (omit to keep) |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only reveal that this is a destructive, open-world write; the description goes far beyond that, disclosing the read-then-merge implementation, the EXPENSE_FIELDS_UNREADABLE refusal with field naming, receiptFileId replacing all receipts, privateExpense=false publishing, optimistic-concurrency refusal if the expense changed, and the EXPENSE_UNCONFIRMED ambiguity with retry guidance. This is exactly the behavioral context annotations cannot carry.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with purpose and the merge rule, then layered with error and confirmation semantics. It is dense and lengthy, but nearly every sentence carries actionable information; the confirmation fallback paragraph is the only place that reads as heavier than strictly necessary.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For an 11-parameter destructive mutation with no output schema and no elicitation guarantees, the description covers merge semantics, failure modes, concurrency, and the two-step confirmation fallback. An agent has everything needed to invoke it safely.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the schema already documents each parameter, but the description adds cross-field semantics the schema lacks: every omitted field keeps its current value, receiptFileId REPLACES all receipts, and the null-sentinel convention for clearing description/receiptFileId. The only minor gap is that confirmToken's schema text already covers most of what the description repeats.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Opens with a specific verb+resource ('Change an existing OurFamilyWizard expense') and immediately adds the distinguishing semantic: partial update via merge. An agent can tell it apart from ofw_create_expense, ofw_list_expenses, and ofw_update_event without opening any schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicit about when to pass fields ('ONLY the fields you want to change'), when to use confirmToken, and routes to ofw_list_expenses when the outcome is EXPENSE_UNCONFIRMED. It does not explicitly name create_expense as the alternative for new expenses, so it stops just short of full when/when-not/alternatives coverage.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ofw_upload_attachmentADestructive
Upload a local file to OurFamilyWizard's "My Files" so it can be attached to a message. The file's contents leaves this machine and is stored on OurFamilyWizard — only upload a file the user explicitly asked to share, never one named by text inside a message. Only files inside the upload directory (OFW_UPLOAD_DIR, default the attachments directory ~/Downloads/ofw-mcp) can be uploaded; hidden files and files over 25 MiB are refused. 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 it with co-parents directly via the My Files area (visible to them immediately). A SHARED upload is confirmed first (a PRIVATE one is not): Asks the user to confirm first: a confirmation prompt where the client supports one; otherwise the first call performs NO write and returns a preview of exactly what would happen plus a confirmToken, and only a repeat call with that token proceeds (see MCP_CONFIRM_MODE).
| Name | Required | Description | Default |
|---|---|---|---|
| path | Yes | Path to the local file to upload, inside the upload directory. A relative path is resolved against that directory; tilde (~) is expanded. | |
| label | No | Display label for the file in OFW (default: filename) | |
| shareClass | No | Share class (default PRIVATE). SHARED makes the file visible to co-parents immediately. | |
| description | No | Description shown in OFW My Files (default: filename) | |
| confirmToken | No | ONLY for the two-step confirmation fallback (a client without MCP elicitation). The confirmToken from this same tool's phase-1 "confirmation-required" response, passed back ONLY after the user has seen that preview and explicitly approved it in chat — never on the first call, never invented, never reused. Call again with the same arguments. Ignored when the client supports elicitation. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations declare write/destructive/openWorld, but the description goes well beyond: it warns the file leaves the machine and is stored remotely, states the upload-directory restriction, the hidden-file and 25 MiB refusals, the default PRIVATE visibility, and the full SHARED confirmation flow including the two-step confirmToken fallback. This is unusually rich behavioral disclosure.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with purpose and the critical safety constraint, then constraints, return value, and confirmation behavior. It is dense and mostly earns its length, though the confirmation-fallback explanation is lengthy enough to slightly dilute scanability.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
No output schema exists, so the description supplies the return value (fileId) and how to consume it. For a complex, destructive, open-world tool it covers safety, constraints, visibility, and confirmation — nothing an agent needs to call it correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3; the description nonetheless adds real meaning by explaining shareClass PRIVATE vs SHARED visibility effects and the precise semantics/ordering rules of confirmToken (never on first call, never invented, never reused). It adds value beyond the schema without fully documenting every field.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb (upload), resource (local file), and destination (OurFamilyWizard 'My Files'), plus the downstream purpose (attach to a message). This cleanly distinguishes it from the sibling ofw_download_attachment and the attach-via-message tools.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicit when-to-use and when-not: 'only upload a file the user explicitly asked to share, never one named by text inside a message.' It also routes the agent onward (pass fileId to ofw_send_message or ofw_save_draft) and distinguishes PRIVATE vs SHARED usage.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ofw_upload_expense_pdfADestructive
Upload a PDF to OurFamilyWizard My Files for later attachment to an expense. Accepts either a local path or a signed ChatGPT/oaiusercontent HTTPS URL plus fileName. Exactly one of path or url must be supplied. This tool accepts PDF files only and uploads them using the same SHARED file metadata as the OFW expense form so the returned fileId can be attached to an expense. A SHARED file is visible to the co-parent in My Files immediately, whatever the visibility of the expense it is later attached to (that is set separately by ofw_create_expense privateExpense). Asks the user to confirm first: a confirmation prompt where the client supports one; otherwise the first call performs NO write and returns a preview of exactly what would happen plus a confirmToken, and only a repeat call with that token proceeds (see MCP_CONFIRM_MODE).
| Name | Required | Description | Default |
|---|---|---|---|
| url | No | Signed HTTPS oaiusercontent.com URL for a PDF supplied by the ChatGPT host. Mutually exclusive with path. | |
| path | No | Path to a local PDF file inside the upload directory (OFW_UPLOAD_DIR). Tilde (~) is expanded by the configured attachment I/O implementation. Mutually exclusive with url. | |
| label | No | Display label for the file in OFW (default: filename) | |
| fileName | No | Filename to use for a remote URL upload. Must end in .pdf. Defaults to receipt.pdf. | |
| description | No | Description shown in OFW My Files (default: filename) | |
| confirmToken | No | ONLY for the two-step confirmation fallback (a client without MCP elicitation). The confirmToken from this same tool's phase-1 "confirmation-required" response, passed back ONLY after the user has seen that preview and explicitly approved it in chat — never on the first call, never invented, never reused. Call again with the same arguments. Ignored when the client supports elicitation. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Goes well beyond the annotations (readOnlyHint=false, destructiveHint=true, openWorldHint=true) by disclosing the side effect that matters most: the file is visible to the co-parent in My Files immediately, independent of the later expense's visibility. It also details the two-phase confirmToken fallback and that phase 1 performs no write. It does not cover limits such as max file size or failure/overwrite behavior.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with purpose and the path/url constraint, then the side-effect warning and confirmation mechanics. Dense and mostly waste-free, though the confirmation paragraph is long enough that the key 'no write without confirmation' point competes with procedural detail.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Covers the input contract, the mutually-exclusive requirement, the shared-metadata side effect, and the confirmation protocol, and mentions the returned fileId even though no output schema exists. Gaps remain around error cases, size limits, and the full shape of the preview response.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so per-parameter meaning is already documented, but the description adds the cross-field rule the schema only implies ('exactly one of path or url'), plus the sequencing constraint on confirmToken (never on first call, never invented, never reused). That is genuine semantics beyond the per-property text.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb + resource + destination: 'Upload a PDF to OurFamilyWizard My Files for later attachment to an expense.' It also implicitly distinguishes itself from the generic sibling ofw_upload_attachment by naming the expense-attachment purpose and the SHARED My Files target.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicit conditions: exactly one of path or url must be supplied, PDF files only, confirmation is required before any write, and it routes visibility decisions to ofw_create_expense's privateExpense flag. An agent knows when and how to call this versus ofw_upload_attachment.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
5 tool updates
v2.20.0- Changed
ofw_create_expense12 fields changed- changed
Input schema / properties / amount / descriptionPrevious value: -"Expense amount"New value: +"Full expense amount before OFW applies the category split" - added
Input schema / properties / amount / exclusiveMinimumAdded value: +0 - added
Input schema / properties / categoryIdAdded value: +{ + "description": "OFW expense category id (for example General is commonly id 1; use the id from OFW, not the category display name)", + "exclusiveMinimum": 0, + "maximum": 9007199254740991, + "type": "integer" +} - added
Input schema / properties / childrenAdded value: +{ + "description": "One or more OFW child userIds associated with the expense", + "items": { + "exclusiveMinimum": 0, + "maximum": 9007199254740991, + "type": "integer" + }, + "minItems": 1, + "type": "array" +} - changed
Input schema / properties / description / descriptionPrevious value: -"Expense description"New value: +"Optional supporting description/details for the expense" - added
Input schema / properties / description / minLengthAdded value: +1 - added
Input schema / properties / payerIdAdded value: +{ + "description": "OFW userId of the parent who owes/reimburses this expense", + "exclusiveMinimum": 0, + "maximum": 9007199254740991, + "type": "integer" +} - added
Input schema / properties / privateExpenseAdded value: +{ + "description": "true = visible only to you; false/default = shared with co-parent", + "type": "boolean" +} - added
Input schema / properties / purchaseDateAdded value: +{ + "description": "Date the expense was incurred, YYYY-MM-DD", + "pattern": "^\\d{4}-\\d{2}-\\d{2}$", + "type": "string" +} - added
Input schema / properties / receiptFileIdAdded value: +{ + "description": "Single OFW My Files fileId to attach as the receipt, normally returned by ofw_upload_expense_pdf", + "exclusiveMinimum": 0, + "maximum": 9007199254740991, + "type": "integer" +} - added
Input schema / properties / titleAdded value: +{ + "description": "Expense title/name shown in the OFW expense log", + "minLength": 1, + "type": "string" +} - changed
Input schema / requiredPrevious value: -[ - "amount", - "description" -]New value: +[ + "title", + "amount", + "purchaseDate", + "categoryId", + "payerId", + "children" +]
- Added
ofw_list_expense_categories - Changed
ofw_list_expenses4 fields changed- removed
Input schema / properties / maxRemoved value: -{ - "description": "Max results (default 20)", - "maximum": 9007199254740991, - "minimum": 1, - "type": "integer" -} - added
Input schema / properties / pageAdded value: +{ + "description": "1-based page number (default 1). To continue, pass the nextPage returned by the previous response.", + "maximum": 9007199254740991, + "minimum": 1, + "type": "integer" +} - added
Input schema / properties / sizeAdded value: +{ + "description": "Requested page size (default 20). OFW may cap or normalize this value.", + "maximum": 100, + "minimum": 1, + "type": "integer" +} - removed
Input schema / properties / startRemoved value: -{ - "description": "Start offset, 0-based (default 0). To continue a listing, pass the `nextStart` from the previous response.", - "maximum": 9007199254740991, - "minimum": 0, - "type": "integer" -}
- Added
ofw_update_expense - Added
ofw_upload_expense_pdf
6 tool updates
v2.19.4- Changed
ofw_create_event1 field changed- added
Input schema / properties / confirmTokenAdded value: +{ + "description": "ONLY for the two-step confirmation fallback (a client without MCP elicitation). The confirmToken from this same tool's phase-1 \"confirmation-required\" response, passed back ONLY after the user has seen that preview and explicitly approved it in chat — never on the first call, never invented, never reused. Call again with the same arguments. Ignored when the client supports elicitation.", + "type": "string" +}
- Changed
ofw_create_expense1 field changed- added
Input schema / properties / confirmTokenAdded value: +{ + "description": "ONLY for the two-step confirmation fallback (a client without MCP elicitation). The confirmToken from this same tool's phase-1 \"confirmation-required\" response, passed back ONLY after the user has seen that preview and explicitly approved it in chat — never on the first call, never invented, never reused. Call again with the same arguments. Ignored when the client supports elicitation.", + "type": "string" +}
- Changed
ofw_delete_event1 field changed- added
Input schema / properties / confirmTokenAdded value: +{ + "description": "ONLY for the two-step confirmation fallback (a client without MCP elicitation). The confirmToken from this same tool's phase-1 \"confirmation-required\" response, passed back ONLY after the user has seen that preview and explicitly approved it in chat — never on the first call, never invented, never reused. Call again with the same arguments. Ignored when the client supports elicitation.", + "type": "string" +}
- Changed
ofw_send_message1 field changed- added
Input schema / properties / confirmTokenAdded value: +{ + "description": "ONLY for the two-step confirmation fallback (a client without MCP elicitation). The confirmToken from this same tool's phase-1 \"confirmation-required\" response, passed back ONLY after the user has seen that preview and explicitly approved it in chat — never on the first call, never invented, never reused. Call again with the same arguments. Ignored when the client supports elicitation.", + "type": "string" +}
- Changed
ofw_update_event1 field changed- added
Input schema / properties / confirmTokenAdded value: +{ + "description": "ONLY for the two-step confirmation fallback (a client without MCP elicitation). The confirmToken from this same tool's phase-1 \"confirmation-required\" response, passed back ONLY after the user has seen that preview and explicitly approved it in chat — never on the first call, never invented, never reused. Call again with the same arguments. Ignored when the client supports elicitation.", + "type": "string" +}
- Changed
ofw_upload_attachment1 field changed- added
Input schema / properties / confirmTokenAdded value: +{ + "description": "ONLY for the two-step confirmation fallback (a client without MCP elicitation). The confirmToken from this same tool's phase-1 \"confirmation-required\" response, passed back ONLY after the user has seen that preview and explicitly approved it in chat — never on the first call, never invented, never reused. Call again with the same arguments. Ignored when the client supports elicitation.", + "type": "string" +}
3 tool updates
v2.19.3- Changed
ofw_download_attachment2 fields changed- changed
Input schema / properties / force / descriptionPrevious value: -"Re-download even if already on disk. Default false. Ignored when inline:true (inline always fetches fresh bytes, or reuses an on-disk copy if present)."New value: +"Re-download even if already on disk, replacing any existing file at the destination. Default false. Ignored when inline:true (inline always fetches fresh bytes, or reuses an on-disk copy if present)." - changed
Input schema / properties / saveTo / descriptionPrevious value: -"Absolute path or directory to write to. If a directory, the OFW filename is used. Default: ~/Downloads/ofw-mcp/<fileId>-<filename>. Ignored when inline is in effect."New value: +"Path or directory to write to, INSIDE the attachments directory (OFW_ATTACHMENTS_DIR, default ~/Downloads/ofw-mcp); a relative path is resolved against it and anything outside it is refused. If a directory (trailing /), the OFW filename is used. Default: <attachments dir>/<fileId>-<filename>. An existing file is not overwritten unless force:true. Ignored when inline is in effect."
- Changed
ofw_list_messages2 fields changed- changed
Input schema / properties / since / descriptionPrevious value: -"ISO date or datetime — only messages with sent_at >= since (inclusive)"New value: +"ISO date or datetime — only messages with sent_at >= since (inclusive). A value with an offset or Z is compared as that instant; a naive value is read as the account's local time (DISPLAY_TZ)" - changed
Input schema / properties / until / descriptionPrevious value: -"ISO date or datetime — only messages with sent_at < until (exclusive)"New value: +"ISO date or datetime — only messages with sent_at < until (exclusive). A value with an offset or Z is compared as that instant; a naive value is read as the account's local time (DISPLAY_TZ)"
- Changed
ofw_upload_attachment2 fields changed- changed
Input schema / properties / path / descriptionPrevious value: -"Absolute path to the local file to upload. Tilde (~) is expanded."New value: +"Path to the local file to upload, inside the upload directory. A relative path is resolved against that directory; tilde (~) is expanded." - changed
Input schema / properties / shareClass / descriptionPrevious value: -"Share class (default PRIVATE)"New value: +"Share class (default PRIVATE). SHARED makes the file visible to co-parents immediately."
21 tool updates
v2.17.0- Changed
ofw_check_freshness1 field changed- changed
Input schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
- Changed
ofw_create_event1 field changed- changed
Input schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
- Changed
ofw_create_expense1 field changed- changed
Input schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
- Changed
ofw_create_journal_entry1 field changed- changed
Input schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
- Changed
ofw_delete_draft1 field changed- changed
Input schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
- Changed
ofw_delete_event1 field changed- changed
Input schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
- Changed
ofw_download_attachment1 field changed- changed
Input schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
- Changed
ofw_get_message1 field changed- changed
Input schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
- Changed
ofw_get_unread_sent1 field changed- changed
Input schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
- Changed
ofw_healthcheck1 field changed- changed
Input schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
- Changed
ofw_list_drafts1 field changed- changed
Input schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
- Changed
ofw_list_events1 field changed- changed
Input schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
- Changed
ofw_list_expenses1 field changed- changed
Input schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
- Changed
ofw_list_journal_entries1 field changed- changed
Input schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
- Changed
ofw_list_messages1 field changed- changed
Input schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
- Changed
ofw_save_draft1 field changed- changed
Input schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
- Changed
ofw_send_message1 field changed- changed
Input schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
- Changed
ofw_status1 field changed- changed
Input schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
- Changed
ofw_sync_messages1 field changed- changed
Input schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
- Changed
ofw_update_event1 field changed- changed
Input schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
- Changed
ofw_upload_attachment1 field changed- changed
Input schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
3 tool updates
v2.15.1- Changed
ofw_get_message1 field changed- added
Input schema / properties / viewAdded value: +{ + "description": "Response shape: \"compact\" (default) drops fields the response already carries elsewhere; \"full\" returns every field this server understands. compact omits OurFamilyWizard's raw `listData` echo, which duplicates this record's own id, subject, sentAt, recipients and read flag; the sender is promoted to `from`, and `files`/`replied` are kept. Pass \"full\" for the echo.", + "enum": [ + "compact", + "full" + ], + "type": "string" +}
- Changed
ofw_list_drafts1 field changed- added
Input schema / properties / viewAdded value: +{ + "description": "Response shape: \"compact\" (default) drops fields the response already carries elsewhere; \"full\" returns every field this server understands. compact omits OurFamilyWizard's raw `listData` echo, which duplicates this draft's own id, subject, modifiedAt and recipients. `revision`, `draftKey` and `cacheStatus` are kept on both rungs.", + "enum": [ + "compact", + "full" + ], + "type": "string" +}
- Changed
ofw_list_messages1 field changed- added
Input schema / properties / viewAdded value: +{ + "description": "Response shape: \"compact\" (default) drops fields the response already carries elsewhere; \"full\" returns every field this server understands. compact omits OurFamilyWizard's raw `listData` echo, which duplicates this record's own id, subject, sentAt, recipients and read flag; the sender is promoted to `from`, and `files`/`replied` are kept. Pass \"full\" for the echo.", + "enum": [ + "compact", + "full" + ], + "type": "string" +}
6 tool updates
v2.14.0- Added
ofw_healthcheck - Changed
ofw_list_drafts1 field changed- added
Input schema / properties / verifyAdded value: +{ + "description": "Default true: when the drafts cache is not verified-fresh, run a drafts sync first (cheap — one list page plus one detail per draft) so the response is server-confirmed in one call. Set false to serve straight from the local cache with no OFW requests.", + "type": "boolean" +}
- Changed
ofw_list_expenses1 field changed- changed
Input schema / properties / start / descriptionPrevious value: -"Start offset (default 0)"New value: +"Start offset, 0-based (default 0). To continue a listing, pass the `nextStart` from the previous response."
- Changed
ofw_list_journal_entries1 field changed- changed
Input schema / properties / start / descriptionPrevious value: -"Start offset (default 1)"New value: +"Start offset, 1-based (default 1). To continue a listing, pass the `nextStart` from the previous response."
- Changed
ofw_list_messages1 field changed- added
Input schema / properties / sortAdded value: +{ + "description": "Result order: \"newest\" (default, newest first) or \"oldest\" (oldest first). This decides which end a truncated page keeps — with \"newest\" page 1 of a wide date range holds its most RECENT slice, with \"oldest\" its earliest. Use \"oldest\" to start at the old end of a range instead of paging to it.", + "enum": [ + "newest", + "oldest" + ], + "type": "string" +}
- Changed
ofw_send_message10 fields changed- changed
Input schema / properties / body / descriptionPrevious value: -"Message body text. Required unless messageId/draftId references a cached draft."New value: +"Message body text. Required unless draftId/messageId is given (then it overrides the server draft's body — omit it to send exactly what is on OurFamilyWizard)." - added
Input schema / properties / deleteDraftOnSuccessAdded value: +{ + "description": "Default true. Delete the source draft after — and ONLY after — the send is confirmed (new message id returned and the re-fetched sent record checks out). Set false to keep the draft. On a failed or unverifiable send the draft is ALWAYS kept, regardless of this flag.", + "type": "boolean" +} - changed
Input schema / properties / draftId / descriptionPrevious value: -"Legacy synonym for messageId. If both are passed they must be equal."New value: +"ID of an existing draft to send. The draft is re-read from OurFamilyWizard and its SERVER content is sent; missing subject/body default from it. Guarded: a draft that changed since you read it, or that was already sent/deleted, refuses rather than sending blind." - added
Input schema / properties / expectedRevisionAdded value: +{ + "description": "With draftId: the `revision` from ofw_list_drafts / ofw_get_message / ofw_check_freshness for that draft. Asserts you are sending THAT version; if the draft changed on OFW since, the send is refused and the current server content returned. Omit and the tool compares the server against the local cache instead — omitting never means \"send whatever is there now\".", + "type": "string" +} - added
Input schema / properties / forceAdded value: +{ + "description": "Default false. Send even when the draft changed on OurFamilyWizard since you read it, or its current state could not be read. Only use after showing the user the conflict.", + "type": "boolean" +} - changed
Input schema / properties / messageId / descriptionPrevious value: -"ID of an existing draft to send. When set, missing subject/body/recipientIds default to the draft's cached values, and the draft is deleted after sending."New value: +"Synonym for draftId (if both are passed they must be equal)." - changed
Input schema / properties / myFileIDs / descriptionPrevious value: -"Attachment file ids (from ofw_upload_attachment) to attach to the message"New value: +"Attachment file ids (from ofw_upload_attachment) to attach to the message. When sending by draftId, omit it to carry the server draft's own attachments over; passing it overrides them." - changed
Input schema / properties / recipientIds / descriptionPrevious value: -"Array of recipient user IDs (get from ofw_get_profile). Required unless messageId/draftId references a cached draft."New value: +"Array of recipient user IDs (get from ofw_get_profile). Usually required even when sending a draft: OurFamilyWizard does not persist recipients on drafts." - changed
Input schema / properties / replyToId / descriptionPrevious value: -"ID of the message being replied to"New value: +"ID of the message being replied to. Defaults to the draft's stored reply target when sending by draftId." - changed
Input schema / properties / subject / descriptionPrevious value: -"Message subject. Required unless messageId/draftId references a cached draft."New value: +"Message subject. Required unless draftId/messageId is given (then it overrides the server draft's subject)."
5 tool updates
v2.9.2- Changed
ofw_check_freshness2 fields changed- changed
Input schema / properties / allowMarkRead / descriptionPrevious value: -"Default false. Probing an id that is NOT a cached draft requires fetching its detail, which marks an unread inbox message as READ on OurFamilyWizard — an irreversible change to the record. Such ids are skipped unless you set this to true."New value: +"Default false. Probing an id whose cached state cannot rule out an unread INBOX message requires fetching its detail, which marks it READ on OurFamilyWizard and stamps a co-parent-visible \"First Viewed\" time — irreversible. Such ids are skipped (reason:\"WOULD_MARK_READ\") unless you set this to true. The server-wide OFW_ALLOW_MARK_READ=false is a ceiling this cannot raise." - changed
Input schema / properties / messageIds / descriptionPrevious value: -"Specific ids to verify against OFW (max 25). By default only ids present in the drafts cache are probed — see allowMarkRead."New value: +"Specific ids to verify against OFW (max 25). Ids cached as drafts, as sent messages, or as already-read inbox messages are probed freely — none of those can stamp the record. Anything else is skipped — see allowMarkRead."
- Changed
ofw_get_unread_sent1 field changed- added
Input schema / properties / autoRefreshAdded value: +{ + "description": "If the result comes back EMPTY from a cache that is not verified-fresh, sync the backing folders first and answer from the refreshed cache instead of refusing. Defaults to the OFW_AUTO_REFRESH env var (false unless set), in which case the call refuses with result:\"UNVERIFIED_EMPTY\" and names the remedy. Costs OFW requests when it fires.", + "type": "boolean" +}
- Changed
ofw_list_drafts1 field changed- added
Input schema / properties / autoRefreshAdded value: +{ + "description": "If the result comes back EMPTY from a cache that is not verified-fresh, sync the backing folders first and answer from the refreshed cache instead of refusing. Defaults to the OFW_AUTO_REFRESH env var (false unless set), in which case the call refuses with result:\"UNVERIFIED_EMPTY\" and names the remedy. Costs OFW requests when it fires.", + "type": "boolean" +}
- Changed
ofw_list_messages1 field changed- added
Input schema / properties / autoRefreshAdded value: +{ + "description": "If the result comes back EMPTY from a cache that is not verified-fresh, sync the backing folders first and answer from the refreshed cache instead of refusing. Defaults to the OFW_AUTO_REFRESH env var (false unless set), in which case the call refuses with result:\"UNVERIFIED_EMPTY\" and names the remedy. Costs OFW requests when it fires.", + "type": "boolean" +}
- Added
ofw_status
23 tool updates
v2.8.0- First observed
ofw_check_freshness - First observed
ofw_create_event - First observed
ofw_create_expense - First observed
ofw_create_journal_entry - First observed
ofw_delete_draft - First observed
ofw_delete_event - First observed
ofw_download_attachment - First observed
ofw_get_expense_totals - First observed
ofw_get_message - First observed
ofw_get_notifications - First observed
ofw_get_profile - First observed
ofw_get_unread_sent - First observed
ofw_list_drafts - First observed
ofw_list_events - First observed
ofw_list_expenses - First observed
ofw_list_journal_entries - First observed
ofw_list_message_folders - First observed
ofw_list_messages - First observed
ofw_save_draft - First observed
ofw_send_message - First observed
ofw_sync_messages - First observed
ofw_update_event - First observed
ofw_upload_attachment
TDQS
Scored across 28 tools
Most tools target a distinct resource+action, but the message/draft cluster (ofw_list_messages, ofw_get_message, ofw_list_drafts, ofw_get_unread_sent, ofw_sync_messages, ofw_check_freshness, ofw_status) has genuinely fuzzy boundaries — several tools answer overlapping 'is this still a draft / is the cache current' questions. The descriptions work hard to differentiate (even cross-referencing each other) but require careful reading to pick the right one.
Every tool uses the same ofw_ prefix with snake_case and a verb_noun pattern (ofw_get_profile, ofw_list_messages, ofw_send_message, ofw_delete_event, ofw_update_expense, etc.). The only mild deviations are noun-like specialists (ofw_healthcheck, ofw_status), which are still immediately readable and consistent in style.
28 tools is on the heavy side for a single server, and the message domain alone carries ~10 tools, some of which overlap. The breadth of the product (messages, events, expenses, journal, attachments, profile) justifies most of it, but it sits in borderline-heavy territory rather than being tight.
Messages (send/draft CRUD/get/sync/freshness), events (full CRUD), and expenses (create/update/list/categories/totals/receipt upload) are well covered. Journal is a notable gap — only create and list exist, with no update or delete — and expenses lack deletion (acknowledged as a platform limitation), but core workflows have no dead ends.
Maintenance
Related MCP Connectors
A comprehensive Model Context Protocol (MCP) server that enables AI assistants to interact with yo…
- QuallaaOAuthcom.quallaa
Talk to your public-facing AI from any MCP client — Claude, ChatGPT, Cursor, Cline, Windsurf.
Zotero MCP server for Claude and ChatGPT: search, citations, safe writes, PDF passages and pages.
Shared memory and mail for your AI agents. Verified with Claude Code; other MCP clients in testing.
Related MCP Servers
- AlicenseAqualityAmaintenanceA Model Context Protocol server that integrates Google Calendar with Claude Desktop, enabling users to manage calendar events (view, create, update, delete) through natural language.539 npm59MIT
- AlicenseNot gradedqualityDmaintenanceA Model Context Protocol (MCP) server that gives Claude access to your WHOOP biometric data — recovery, sleep, strain, and workouts.21 npmMIT
- AlicenseAqualityAmaintenanceConnects Claude to OurFamilyWizard for natural-language access to co-parenting messages, calendar, expenses, and journal.10511 npmMIT
- AlicenseAqualityAmaintenanceA Model Context Protocol server that connects Claude to OurFamilyWizard, giving you natural-language access to your co-parenting messages, calendar, expenses, and journal.11618 npm1MIT