# Maintenance Due: Streamable HTTP MCP Due engine over stateless Streamable HTTP. Due is the log plus the clock. Free because Cloudflare Workers and D1 are uniquely generous. Org is the tenant (solo is a one-member org); the membership bearer and the log leave with you. Live MCP endpoint on this Worker. This endpoint is stateless. Do not send a sticky `Mcp-Session-Id`. DELETE /mcp returns 405 (there is no session to terminate). GET with Accept: text/event-stream returns 405 (no standalone SSE stream). POST JSON-RPC 2.0 to /mcp. Same membership-scoped Bearer as REST. Wrong or revoked bearer → HTTP 401 (does not mint an org). ## Handshake (no session) `initialize` → `notifications/initialized` → `tools/list` / `tools/call` / `ping`. Each POST is independent. You do not need a prior initialize to call tools. Supported `MCP-Protocol-Version`: 2025-03-26, 2025-06-18, 2026-07-28. Responses are JSON envelopes: `summary`, `items[]`, `coming[]`, `upcoming[]`, `snoozed[]`, `hints[]`, `next`. Writes include a due/inbox slice — do not re-fetch. `items[].overdue` is how late when due. `items[].last` / `upcoming[].last` is the last Event (shop, cost, parts, meters). `upcoming[]` has remaining miles/hours, `next_due_at`, `next_due_meters` (the work-order odometer), `pace` (origin→latest per day), and `eta` (leftover / pace). `snoozed[]` is hidden until `snoozed_until` (vacation week; due math unchanged). `get_due as_of` / `within_days` / `within_meters` fill `coming[]` so a Friday board is one hop (`within_days` includes `eta`). `get_due { thing }` (alias `thing_name`; case-insensitive exact) is the Sprinter board; `get_due { name }` (aliases `service` / `job` / `service_name`; case-insensitive exact) is the oil board across the vans; `service_id` is one Service. `get_history { thing }` is that van's log. `get_history { name }` is the oil log across the vans (same name axis; `totals.cost` is every matching Service). `get_history { meter }` is the reading log (fleet miles last week; `totals.used` is last − first per Thing, then summed). `get_history` names `totals.cost` on `since` / `until` / `shop` so last year's shop spend is one hop. `list_things` names `things[].meters` so a fleet odometer roll-call is one hop (`get_thing` names the same field). `list_services { thing }` / `get_thing { thing }` is the Sprinter catalog (id, name, rule, last). `list_services { name }` is the oil jobs across the vans (last on each van). `append_event { thing, name }` is oil on the Sprinter (do not list_services just to recover `svc_…`). `append_reading { thing }` is miles on the Sprinter (do not list_things just to recover `thg_…`). `archive_service { thing, name }` is Cabin filter on the Sprinter (do not list_services just to recover `svc_…`; oil stays). `snooze_service { thing, name, until }` is Trash night on the Sprinter (do not list_services just to recover `svc_…`; due math unchanged). `set_rule { thing, name }` is Grease on the Civic (do not list_services just to recover `svc_…`; only if they stated an interval). `create_service { thing, name }` is Brake fluid on the Civic (do not list_things just to recover `thg_…`; thing_id still works; one live Thing does not need thing). `update_thing_steerage { thing, steerage }` is Always Eastside on the Civic (do not list_things just to recover `thg_…`; thing_id still works; one live Thing does not need thing; do not stash a lasting shop preference in Thing notes). `format=md` on get_due / get_history / list_inbox / get_message keeps the previous Markdown bodies (Muse). ## Public tools (no auth) - health - bootstrap { seed?: true, name?, email? } — mint org + owner token; seed returns Truck A due + next append_event. Omit Authorization; a present bearer does not mint. - accept_invite { token } / decline_invite { token } — no Authorization. Accepting joins under RBAC. ## Authenticated tools (Authorization: Bearer ) - list_things dashboard: meters (live dash) + due_count + next_due (overdue when currently due; remaining / next_due_at / next_due_meters / last when nothing is currently due). include_archived?: true - get_thing { thing_id? | thing? } — thing + meters (live dash) + services[] (id, name, rule, last) + steerage snippets + archived. thing (alias thing_name) is the Sprinter (case-insensitive exact) - list_services { thing_id? | thing? | name?, include_archived? } — catalog (id, name, rule, last). last is the last Event (shop, cost, parts, meters), including Notes-only jobs. thing is the Sprinter catalog. name is the oil jobs across the vans (last on each van). Not the due board. Omit both for the account. - create_thing { name, type?, notes? } - archive_thing { thing_id } — sold/retired; history stays; due drops it; returns remaining due - unarchive_thing { thing_id } — back on the due list - create_service { thing? | thing_id?, name, notes?, rule?, reading?, readings?, last_done_at?, last_done_meters? } — thing is Brake fluid on the Civic (same name axis as get_due / append_reading; do not list_things just to recover thg_…); thing_id still works; one live Thing does not need thing; calendar every_months | every_days | each_year_on | each_month_on | each_month_on+every_months | each_weekday | each_weekday+every_weeks; meter every | below | above; reading is the dash; last_done_meters is last oil (not an Event); last_done_at starts the calendar clock (not an Event); do not invent intervals - archive_service { service_id? | thing? + name? } — thing + name is Cabin filter on the Sprinter (same name axis as get_due / append_event; do not list_services just to recover svc_…); service_id still works; retired one job; history stays; due drops that Service; oil stays - unarchive_service { service_id? | thing? + name? } — that Service is back on the due list; same name axis as archive_service - snooze_service { service_id? | thing? + name?, until } — thing + name is Trash night on the Sprinter (same name axis as get_due / append_event; do not list_services just to recover svc_…); service_id still works; hide until a stated clock; due math unchanged; do not invent an Event - unsnooze_service { service_id? | thing? + name? } — clear the snooze; back on the due list; same name axis as snooze_service - set_rule { service_id? | thing? + name?, expression, last_done_at?, last_done_meters? } — thing + name is Grease on the Civic (same name axis as get_due / append_event; do not list_services just to recover svc_…); service_id still works; only if they stated an interval; annual = every_months: 12; seasonal date = each_year_on: "10-15"; day of month = each_month_on: 1; every 3 months on the 1st = each_month_on: 1 plus every_months: 3; weekday = each_weekday: "tue"; every other Saturday = each_weekday: "sat" plus every_weeks: 2; gauge line = below: 20; last_done_at is the stated last-done date; last_done_meters is last oil (empty Event log only) - append_reading { thing? | thing_id?, meter, value } or { readings: [{ thing?, meter, value }, ...] } — thing is miles on the Sprinter (same name axis as get_due; do not list_things just to recover thg_…); thing_id still works; one live Thing does not need thing; returns due + upcoming - append_event { service_id? | thing? + name?, completed_at?, parts?, notes?, shop?, cost?, reading?, readings? } — thing + name is oil on the Sprinter (same name axis as get_due; do not list_services just to recover svc_…); service_id still works; shop + cost are the receipt (vendor aliases shop); reading logs miles/hours on the same write and stores event.meters; returns due + upcoming - get_due { thing_id?, thing?, service_id?, name?, all?, expand?, format?, as_of?, within_days?, within_meters? } — thing (alias thing_name) is the Sprinter board (case-insensitive exact Thing name; same full board as thing_id); name (aliases service / job / service_name) is the oil board (case-insensitive exact); service_id is one Service; items[] due on this clock (overdue is how late; last is shop/cost/parts/meters); snoozed[] until snoozed_until; as_of moves the calendar; coming[] is within_days / within_meters (within_days includes eta); upcoming[] remaining / next_due_at / next_due_meters / pace / eta / last - get_history { thing_id? | thing? | service_id? | name? | meter?, since?, until?, shop?, before?, limit?, format? } — thing (alias thing_name) is one van by name (the Sprinter log); meter (alias gauge) is the reading log (fleet miles last week; totals.used is last − first per Thing, then summed); name (aliases service / job / service_name) is the oil log across the vans (case-insensitive exact); service_id is one Service (shop + cost + meters); thing_id is the mixed log; omit the id with since / until / shop for account spend; totals.cost is the filter, not the page; capped; truncated + cursor - get_steerage — full account notes (empty default) - update_steerage { steerage } — replace; null clears; not an Event - get_thing_steerage { thing? | thing_id? } — full thing notes; thing is Always Eastside on the Civic (same name axis as get_due / create_service; do not list_things just to recover thg_…) - update_thing_steerage { thing? | thing_id?, steerage } — thing is Always Eastside on the Civic; thing_id still works; one live Thing does not need thing; null clears; not an Event; do not stash a lasting shop preference in Thing notes - claim_mailbox { address, display_name? } — morgan@maintenancedue.com; never hello@; returns inbox - list_inbox { mailbox_id?, limit?, before?, format? } - get_message { mailbox_id, message_id, format? } - send_mail { mailbox_id, to?, subject?, text, html?, in_reply_to? } — in_reply_to may be msg_… - submit_feedback { kind: issue|improvement, title, body, how_found? } — issue requires how_found; ops queue; no account_id - get_org / list_members / list_invites - invite_member { email, role? } — owner/admin; email + runnable prompt - revoke_member { membership_id? | email? } — offboard now; leftover bearer is 401 - leave_org ## Resources (text/plain) - maintenance-due://skill - maintenance-due://agent-prompt - maintenance-due://llms REST companions remain: JSON writes on /api/v1, Markdown reads on /api/v1/agent/*.