Huishouden connector
The MCP server runs as a Cloudflare Worker with OAuth 2.1 via workers-oauth-provider, Workers KV for encrypted grant storage, Workers Rate Limiting bindings, and Durable Objects for Firestore read budgets.
Uses Firebase Authentication for Google sign-in and to verify refresh/ID tokens, and calls Firestore REST as the signed-in person so the household's Firestore rules apply.
Sign-in happens through Google (Firebase Auth) on Huishouden's own site, so the connector acts as the signed-in person.
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., "@Huishouden connectorwhat's on today, and add milk to the groceries list?"
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.
Huishouden connector
The connector is a remote MCP server. People add it to their own AI assistant (Claude, ChatGPT, Gemini CLI or any MCP client) to use their Huishouden household from there. They can ask "what's on today?", add groceries, log a pet's feed or a medicine dose, see upcoming bills, or look up the medicines of someone they care for and research them in the assistant.
The connector is free, it works for any household, and each person approves it for themselves. The household's own Firestore rules decide everything it can do, because it acts as the signed-in person. It never uses a service account.
MCP endpoint:
https://huishouden-connector.<account>.workers.dev/mcp(Streamable HTTP). Staging runs athuishouden-connector-staging.<account>.workers.dev, against thehuishouden-stagingproject.How to add it: the portal's Use with your AI assistant page (
/assistant, in every app's account menu) shows the exact address and the steps for Claude, ChatGPT and Gemini CLI, in English, Spanish and Dutch.
Tools
Every tool takes an optional household (the default is the one the apps open), lang (en, es
or nl; the default is the person's Huishouden language, the one they set in the apps,
households/{id}.langs) and time_zone (the default is the one in their profile). Answers are
short Markdown in that language, with deep links into the apps. They also come back as structured
data. What a tool publishes (a Health visit's agenda item and reminders) is written in that
language, with texts only for its readers' other languages. Writing tools take an optional idempotency_key: a retry with the
same key writes nothing new.
Tool | Does |
| Who is signed in, their households and their role in each, language and time zone |
| The household's home address (or only its neighbourhood, when approximate) and time zone; every member reads it, helpers and kids included |
| Today's agenda (overdue, today, next 48 hours) and open to-dos, as the portal shows them |
| The household calendar between two days, by day |
| Open to-dos from every app, with ids, filters and sorts, and whether this person may act on each |
| Run a to-do's Done or Cancel action exactly as the portal does (kit |
| Shopping lists, in the Groceries app's shapes (including staples and each item's need-by day, |
| Change an item still to buy: the day it is needed by (or none), Standard or Whenever, quantity or notes. Helpers and kids change only the items they added |
| A task, with an optional due date or time |
| Open bills, amounts, due dates, autopay and pay links (admins and members only) |
| The feeding board, care reminders, medicine courses and outings (bathroom breaks, walks); logging as the Pet app does |
| Upkeep jobs due and regular events; a new regular event (EventRule) or a booked visit |
| Baby's settings: the instructions from the provider on the contraction timer, and the kick counter's target and daily reminder (a new time reschedules the next 7 days' reminders, as Baby does). Admins and members change them; helpers read them; kids get neither |
| Pet, baby or car: in that app's list, on the household calendar ( |
| The household's shared contacts |
| The people this person looks after in Health |
| Strength, dose, schedule, prescriber, pharmacy, supply and days left, refills, notes, allergies |
| Given, missed and skipped doses, and adherence per medicine and overall |
| Today's doses, as-needed availability, refills running low |
| Given or skipped, with Health's double-dose and as-needed guards (it asks before overriding) |
| Add or change a medicine, stop or restart it, count the supply, mark a refill ordered |
| Health's printable medicine list, as Markdown, with the person's current conditions for admins and member carers |
| A person's visits, coming up and past (attended or missed), with doctor (a doctor marked private only for admins and members), place, prep, reminders, follow-up and medical area; the condition it is about and the notes only for admins and member carers |
| A person's (or everyone's) conditions grouped by medical area, optionally one area ("Nan's neurology conditions"): ICD-10-CM code, status, when diagnosed, by whom and where, severity, the medicines that treat it, notes. Admins and member carers only (the person too, when a member); helper carers get none |
| Add a condition (admins and member carers), filed under its ICD-10-CM code's medical area, else its name's, unless |
There are no delete tools. Every Health answer ends with a one-line note: these are the household's own records, not medical advice.
The tools live in the kit, @huishouden/pwa-kit/household-tools: hh data (huishouden/cli) runs
the same implementation from a terminal, so the two can't drift. This repo adds the MCP server, the
sign-in, rate limits and the audit log around them, and tests every tool under the household's
rules against the emulators.
Related MCP server: stafett
How sign-in works
The connector speaks OAuth 2.1 as MCP clients expect: discovery (RFC 9728 and RFC 8414), dynamic
client registration and Client ID Metadata Documents, PKCE (S256), and refresh tokens. All of this
comes from @cloudflare/workers-oauth-provider.
The identity step happens on Huishouden's own site, through the same Google sign-in as every app, so
there is no OAuth client to configure and no redirect URI to register.
GET /authorizeshows the connector's consent page: which app is asking, where access goes, and a warning for local apps. The page can't be framed and is bound to the browser.Continue sends the browser to the portal's
/connectpage, with a state bound to that browser.The portal signs the person in with Google (Firebase Auth) and asks them to confirm. On Allow it posts their Firebase refresh token to
/connect/hand-off, from the portal's origin only. The connector checks the token with Firebase Auth and keeps it for two minutes under a one-time code, encrypted with a key only that code derives.The portal sends the browser to
/connect/callback. The connector takes the hand-off back once, for that state, and completes the grant. The refresh token goes into the grant's props, which workers-oauth-provider stores in Workers KV encrypted with a key that only the client's tokens unwrap.On each tool call, the refresh token buys a fresh ID token (cached per isolate). Firestore REST is called with that ID token, so the rules apply exactly as in the apps. Each OAuth refresh also checks the Firebase sign-in still stands. When it doesn't, the grant ends and the assistant asks the person to connect again.
hh login
The hh command line signs in through the same portal page, with a loopback address and a PKCE
proof key in place of a browser cookie (pwa-kit docs/server.md "Signing in from a command line"):
Endpoint | Caller | Does |
| the portal (CORS: its origin only) | Takes |
|
| Takes |
The browser and server halves are reusable kit modules, documented in pwa-kit docs/server.md:
Module | Provides |
| The portal's /connect flow, and |
| Every tool, shared with |
| Refresh token to ID token; verifying a portal ID token |
| Firestore as the person: get, query, atomic commit |
| The person's days on a UTC server |
| Reading the household's home ( |
| The data contracts and app logic |
The calendar feed reuses the same modules.
Safety
Rules: every read and write is the person's own and checked by huishouden/rules.
Helpers and kids get only what isn't private, and never money.
Health is readable only by admins, the person's carers and the person themself. Others get nothing, as if the person didn't exist.
Records the connector creates carry
via: 'assistant', and the rules accept only that value. (hh dataleaves it out: the person is typing.)
Rate limits: each connected assistant may make 60 tool calls and 20 writes a minute (Workers Rate Limiting bindings).
Read budgets: Firestore's free plan has 50,000 reads a day for every app and Worker together, and at that limit every read fails until midnight Pacific. The connector runs every tool call on the kit's
ReadBudget(pwa-kit STANDARD.md "Read budgets", which allocates the day): a call may be billed for at most 500 reads (CALL_READS), the connectorFIRESTORE_CONNECTOR_READSa day (4,000) and each connectionFIRESTORE_CONNECTION_READS(1,000), counted per Pacific day in theReadBudgetDurable Object (the kit's SQLite ledger;src/reads.ts,src/read-budget.ts). A read is counted as Firestore bills it, from each answer (the kit'sbilledReads). A call over any budget, or one that meets Firestore's own quota, answersfirestore-quota, as the calendar Worker's API does: an error result whose data is{ error: 'firestore-quota', scope: 'call' | 'connection' | 'connector' | 'project', resetsAt }(noresetsAtforcall: the next call starts afresh), with text in the person's language. Firestore's own quota also pauses every call for 15 minutes, which answerprojectwithout reading. A budget met mid-call refuses the call's later reads and writes, and the whole answer is the quota's. A write already saved keeps its own answer. A request's tool calls run one at a time. Reads a call makes at once all go out before any is counted, so the day can end a call's reads over. Unset or0: no limit. If the Durable Object can't be reached, calls go ahead uncounted and aread-budgetlog line says so. Each read tool has a read-budget test on the fixture household (test/tools/budgets.test.ts); a new read tool fails it until it has one.Audit: each tool call is written, as the person, to
households/{id}/connections/{connection}/audit: the tool, read or write, whether it worked, the app and record. Only that person can read it. The portal shows it under each connected assistant, next to Disconnect.Revoke: the portal calls
POST /connections/revokewith the person's Firebase ID token. The connector checks the token with Firebase Auth, revokes the grant and its tokens, and removes the connection record.GET /connectionslists the person's grants.Logs: each tool call logs the tool name, read or write, the outcome, the duration, the Firestore reads it was billed for and an error code (
firestore-quotafor a budget). Logs never contain names, emails, record ids, tokens, medicine names or what a visit is. The connector sends nothing to New Relic.
Development
bun install
bun run lint # tsc, src and tests
bun run test # unit, Firebase Auth, hh login's endpoints, and every tool against the Firestore and Auth emulators (JDK 21)
bun run e2e # wrangler dev against the emulators + the MCP SDK client: the whole OAuth sign-in, then tools
bun run dev # wrangler dev (set FIREBASE_API_KEY in .dev.vars)The tests use the household's real rules from huishouden/rules main. Set RULES_REF to use
another ref, or RULES_FILE to use a local checkout. The household in test/fixtures/household.ts
is invented: every role, a non-member, and Health with a carer and a non-carer.
Deploy
bunx wrangler secret put FIREBASE_API_KEY # the project's public web API key, once
bunx wrangler secret put FIREBASE_API_KEY --env staging
bun run deploy && bun run deploy:stagingThe connector needs no other secrets. Workers KV (grants), the rate limits, the read budgets'
Durable Object (SQLite-backed, created by the deploy's [[migrations]]) and Workers itself all fit
the Cloudflare free plan.
Deploy from GitHub Actions (optional)
CI tests main on every push and manual run. It deploys staging and then production on main once the production environment has these
secrets; until then the deploy job runs but deploys nothing, with a notice. Repository secrets of the same names also reach the job; once the environment holds the values, delete them (gh secret delete CLOUDFLARE_API_TOKEN -R huishouden/connector, and gh secret delete CLOUDFLARE_ACCOUNT_ID -R huishouden/connector). The environment itself (Settings > Environments > production, deployment branches: main) exists already.
Cloudflare dashboard > My Profile > API Tokens > Create Token > "Edit Cloudflare Workers" template, limited to this account.
Add the token as
CLOUDFLARE_API_TOKENand the account id asCLOUDFLARE_ACCOUNT_ID, secrets of theproductionenvironment (deployment branches:mainalone, so no other branch's run can read them):hh ops secret set connector CLOUDFLARE_API_TOKEN --env productionandhh ops secret set connector CLOUDFLARE_ACCOUNT_ID --env production, each value on stdin.
License
Source available under PolyForm Shield 1.0.0: you may use, study and modify this code for any purpose except providing a product that competes with Huishouden.
Huishouden and its logo are the project's brand; please don't use them for other products.
This server cannot be deployed
Maintenance
Related MCP Connectors
Remote MCP server for supportsheep: run AI interviews and manage support content for your blog.
- ZapierOAuthcom.zapier
Hosted MCP server connecting AI assistants to 9,000+ apps and 40,000+ actions via Zapier.
MCP server for your apps' tools and custom tools, plus hosted AI agents and approval-gated workflows
Hosted MCP server exposing US hospital procedure cost data to AI assistants
Related MCP Servers
- AlicenseNot gradedqualityBmaintenanceMCP server that gives AI agents full control over grocery lists, todos, and packing lists. Your AI creates lists, adds items, checks them off, and shares with family/friends.4MIT
- FlicenseNot gradedqualityCmaintenanceA remote MCP server that gives AI assistants a shared memory, enabling structured handoffs, journaling, and notes for seamless continuation across sessions.-
- AlicenseNot gradedqualityCmaintenanceAn MCP server for managing a home medicine box, enabling AI assistants to query medicines, recognize images, compute expiry dates, and get medication advice via stdio JSON-RPC.MIT
- AlicenseNot gradedqualityBmaintenanceA self-hosted MCP server that gives an Alexa+ household a durable operational memory for home repairs, warranties, and service cases, enabling multi-turn troubleshooting, provider booking, and reminders.Apache 2.0