Last updated 12 September 2026.
Saybooks is a place Claude can keep your records, under rules it cannot break, with everything it does written down where you can check it. This page covers what a person needs: connecting Claude, what keys and roles mean, spaces and their modules, documents, personal finances, getting your data out, and running it yourself. The rules themselves are in the specs.
Claude Code: claude mcp add books -t http https://saybooks.io/mcp, then /mcp in a session to sign in. Any client that speaks MCP over streamable HTTP with OAuth works the same way; the endpoint publishes its discovery metadata and accepts both dynamic registration and metadata-document client ids.
Claude allows one connector per address, and one Saybooks connection opens one space. So each kind of books has an address of its own, and you can hold several connectors side by side, say invoices and a job hunt:
https://saybooks.io/mcp | any of your spaces; consent asks which |
https://saybooks.io/mcp/invoices | your invoicing spaces only |
https://saybooks.io/mcp/hunt | your job-hunt spaces only |
https://saybooks.io/mcp/books | your full order-to-cash spaces only |
https://saybooks.io/mcp/s/<space id> | exactly that space, for two of the same kind; the id is the ws= part of the space's URL |
Each one signs in the same way and mints the same kind of key; the address only decides which spaces consent offers.
If you are building a server of your own and want the same shape: every door publishes its own protected-resource metadata at /.well-known/oauth-protected-resource/<path> and answers 401 with a pointer to it. The client sends that URL back as the resource parameter (RFC 8707) on the authorization request; the server stores it with the pending request and the consent page reads the path from it. Claude sends it. The code is in src/oauth.js.
A script, a second agent, or a client without OAuth can use a key directly. In the sidebar open Share this space…, type a name for the agent, pick its role, press Mint key, and use the URL that appears: https://saybooks.io/mcp/m-…. The link is the key: whoever holds it acts as your delegate with that role. Revoke it from the same dialog; the connection made through sign-in above is a key of exactly this kind, minted for you at consent, and shows up in the same list.
Saybooks is a plain MCP server, so a model on your own machine can keep the books with nothing leaving it. Tested on a 32 GB MacBook with LM Studio and Gemma 4 26B A4B (the QAT 4-bit MLX build): a full invoice cycle first time, and a full statement cycle. What that took is its own note.
git clone, npm install, node server.js for the workbench at localhost:8140. The stdio MCP server is mcp-server.js in the same folder.mcp.json:
{ "mcpServers": { "saybooks": {
"command": "/usr/local/bin/node",
"args": ["/path/to/saybooks/mcp-server.js"],
"env": { "SAYBOOKS_WORKSPACE": "main", "SAYBOOKS_MODULES": "core,solo", "SAYBOOKS_ACTOR": "gemma-4" }
} } }
Use the absolute path to node. SAYBOOKS_MODULES picks the tools the model sees: core,solo is 35 tools for invoicing, core,purchases 48 for statements; all eight modules is 144 and too many for a small model. SAYBOOKS_ACTOR is the name on the audit trail.What to expect. A 26B model does one step at a time reliably and relays refusals in the system's words. It will sometimes stop mid-turn on LM Studio's current Gemma template; "continue" recovers it. It will occasionally report success on a refusal, which is what the audit trail is for, and it once invented a purchase order number, which is what the preview before issue is for. The rules held on every run. The plain-language scripts we test with are in test/local/: npm run local -- --model <id> --script plain-invoicing --instructions off.
Say what happened, in your own words. Claude calls the same commands the buttons call, under the same rules, and every action lands in your audit log attributed to the agent.
Claude cannot invent an amount, a date, a name or an address: when it does not have a fact it asks you, and if it tries to write one anyway the system refuses. Everything Claude does also appears in your browser, as normal pages, the moment it happens.
| Role | Can | Cannot |
|---|---|---|
| owner | Everything, including inviting people, minting keys, setting credit limits, deleting the space | — |
| controller | Every business act: customers, orders, invoices, payments, credit authority | Invite, mint, delete the space, change the company profile |
| clerk | Day-to-day writes: orders, shipments, invoices, payments | Credit authority, everything an owner keeps |
| viewer | Read everything | Write anything |
A key is minted for a role. An agent holding a controller key is a controller; a command outside its role is refused with a sentence naming who to ask, and the attempt is logged. People are invited by email and sign in with Google; agents get keys. Both show up in the audit log by name.
A space is one set of books with its own rules, its own members and its own audit log. Every space chooses its modules when it is made: invoicing, order to cash, CRM, job hunt, personal finances, any mix. The free doors, invoicing and a job hunt, are that chooser with one box ticked. Your first sign-in keeps the demo books as a space called Demo books and opens the chooser for your real one. You can have as many spaces as you like, and each key opens exactly one. Signing in again lands you in the space you used last.
Anonymous demo sandboxes at /app?demo=1 are private to your browser, seeded with example books, and deleted after 24 hours. Sign in while one is open and you keep it.
.pdf appended. The page shows the PDF's own pages, so what you preview is exactly what prints: same pagination, same fonts. Paper is A4 unless the company is in the US, Canada or Mexico. A line's first sentence prints as its title, anything after a line break prints smaller beneath it.INV-{NNNN} by default, INV-{YYYY}-{NNN} to restart each year.Full-books spaces carry a Purchases area: what you buy, from bank and card statements and receipts your agent reads. Nothing in Saybooks parses a file. The agent hands over the rows and the balances the statement prints; the statement is accepted whole when the rows reconcile to those balances, or refused with the gap named. Every row remembers its statement, its row number and the line as read. The same file is never imported twice; rows already on record are skipped and listed back.
Saybooks keeps the operational record; QuickBooks, Xero or your accountant's own system stays the ledger of record. The Ledger pages hand one to the other. The journal is derived from what happened, never posted or stored, so it is always current.
The column layouts follow each product's published import template. If your import wants a column spelt differently, tell us and it is a one-line change: the formats are a table in src/modules/bridge/views.js.
Every rule in your books applies to Claude exactly as it applies to you. When a command is not allowed, the answer is one sentence with the reason, shown identically on the button's tooltip, in the thrown error and in the agent's reply, and written to the audit log. A real one:
Claude relays it and asks a person what to do. It does not split the order or quietly raise the limit; raising a limit is its own logged act by someone with the authority.
Saybooks is open source under AGPL-3.0. Node 20 and nothing else.
git clone https://github.com/alviso/saybooks.git
cd saybooks && npm ci
node server.js # workbench at http://127.0.0.1:8140, one local workspace, you are the owner
npm test # the 16-gate contract and every conformance scenario
Environment: SAYBOOKS_PORT (default 8140); SAYBOOKS_DEMO=1 turns on the hosted mode with sandboxes, Google sign-in and the public pages; SAYBOOKS_PUBLIC_URL is the origin printed into document links; SAYBOOKS_ADMIN is the Google account allowed to open /admin. Google sign-in needs OAuth client credentials; see src/auth.js. A Dockerfile is included and is what runs saybooks.io.
Local MCP for development: node mcp-server.js speaks stdio. Over HTTP, each key's URL is its own endpoint; a sandbox's bare id is a key too.
No. It produces documents and records payments you received. It never emails anyone, never charges a card, never moves money. You send the link or the PDF yourself.
It hands over, rather than syncing. The Ledger pages export a period as a Xero manual journal, QuickBooks Online journal entries, or plain CSV, mapped to your accountant's own chart of accounts, and every hand-over keeps its control totals and the exact file. Nothing is posted into your ledger over an API, and nothing is read back out of it.
Any currency your company declares, and each invoice carries its own. What it does not do is convert between them: a period holding two currencies comes back grouped by currency rather than translated, and a ledger hand-over covering two is refused until you name one.
Yes. Every command has a screen in the web workbench, and a click and a sentence to Claude are the same event, logged and refused the same way. The agent is the fast path, not the only one.
No. It keeps the operational record upstream of the ledger: quotes, orders, shipments, invoices, receivables, pipeline, and reviewed spending from your bank statements. Your accountant's system stays the ledger of record and the hand-over feeds it.
Export takes everything with you, in JSON, audit log included, any day. The code is public under the AGPL and runs on a laptop. Your data does not depend on the hosted service existing.
hello@saybooks.io, or open an issue on GitHub. If it is a rule that let something through that it should have refused, say so first: that is the bug we care most about.