backoffice
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., "@backofficewhy don't yesterday's POS room charges match the PMS?"
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.
backoffice-copilot
A hotel finance team's morning in one screen: the checks they'd otherwise do by hand in spreadsheets, and a Claude copilot that investigates anything red, shows its working, and asks before it changes anything.

Demo mode: the model's replies are recorded so the app runs without an API key. The MCP tools, database, approval flow, dashboard and audit log are all real.
What it does
Morning checks. Every morning finance asks the same questions. Did every POS check charged to a room reach the PMS? Does card clearing balance? Are there duplicate supplier invoices? Is ad spend on budget? The dashboard answers them for the business date, next to occupancy, ADR, RevPAR and F&B revenue.
The copilot. Click Ask copilot on a red check:
"Why don't yesterday's room charges in the POS match the PMS?"
The copilot compares the POS room-charge checks with the PMS interface postings, finds check 50608 (Pool Bar, R385.00, room 214, closed at 23:47) never reached the PMS, and proposes posting it to the guest's folio. Nothing changes until a person clicks Approve. Then the check on the dashboard turns green.
flowchart LR
U[Finance user] <--> UI[Dashboard + chat<br/>checks, tool calls, approval cards, audit log]
UI -- /api/overview --> C[checks.py]
UI <-- SSE --> A[Agent loop<br/>Claude API, tool use]
A <-- MCP over stdio --> S[backoffice MCP server]
C --> S
S --> DB[(PMS statistics · POS takings and room charges<br/>card settlements · bank exceptions<br/>supplier invoices · ad spend)]
A -- write tools --> G{Human approval}
G -- approve --> S
A --> L[(Audit log)]The dashboard and the copilot use the same tested functions in the MCP server, so they can never disagree about a number.
Related MCP server: manager-mcp
Planted problems to find
Check | Problem | Fix the copilot proposes |
Room charges: POS → PMS | A R385.00 pool bar check charged to room 214 at 23:47 never reached the PMS | Post it to the guest's folio |
Card clearing | R20.00 short: a refund done on the pool bar card terminal, not in the POS | Journal Dr Sales refunds / Cr Card clearing, and resolve the bank line |
Supplier invoices | Coastal Linen billed R8,740.00 twice, three days apart | Put the second invoice on hold |
Ad spend | Google Ads projected R1,220.00 over October's budget | Nothing: that's marketing's decision, so it only reports |
How it keeps a finance team safe
Rule | How |
Read freely, write only with approval | Each MCP tool says what it is: |
The model never does the maths | Every total, VAT split, difference, ADR and projection is calculated in the MCP server with |
And we check that it didn't | The evaluation set fails any answer containing a rand amount that no tool returned. |
Guard rails on writes | Journals must balance, use real accounts, carry a reason and stay under R1,000. A room charge can't be posted to a folio twice. |
Errors are useful | Bad dates or accounts come back to Claude as clear tool errors so it can correct itself, instead of failing the conversation. |
Everything is audited | Every tool call, approval request, approval and rejection is logged with time, tool, inputs and who decided. |
Rejection is respected | A rejected action goes back to Claude as "the user rejected this", and it isn't proposed again. |
Live mode can't run up a bill | Messages are capped per conversation and per day. Set a spending limit on the API key as well. |
More detail in docs/DESIGN.md. How I'd scope this for a real hotel,
written for the finance manager who'd sign it off: docs/SCOPE.md.
Evaluation set
evals/cases.json has seven questions with known-correct outcomes:
the four planted problems, a rejected fix, yesterday's trading, and a date with no data.
Each case runs on a fresh database and checks that the copilot:
called the right tools,
proposed the right change (or none, when the decision belongs to someone else),
didn't propose a rejected change again,
mentioned the key facts,
used only amounts that a tool returned,
left the data in the right state afterwards (for example card clearing balanced).
python -m copilot.evals # recorded answers: free, runs in CI on every push
python -m copilot.evals --live # the Claude API: run before changing the prompt, a tool or the modelWhat's in it
Part | File |
MCP server: 8 read-only tools, 3 tools that need approval, 1 resource | |
Morning checks and headline numbers for the dashboard | |
Agent loop: Claude tool use, pause/resume for approval, audit | |
MCP client over stdio (and an in-process option for tests) | |
FastAPI app: dashboard API, chat over Server-Sent Events, reset, live-mode limits | |
Dashboard and chat UI | |
Demo mode: recorded answers that still run the real tools | |
Evaluation runner and cases | |
Fictional hotel data with planted problems |
The PMS and POS data is shaped like a hotel PMS such as Opera (daily room statistics, charges received through the POS interface) and a POS such as Simphony (checks closed to a room). It is not connected to either product.
Run it
python -m venv .venv && source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -e ".[dev]"
python -m pytest -q # no API key needed
uvicorn copilot.web:app # demo mode, open http://localhost:8000For live answers from Claude, set ANTHROPIC_API_KEY before starting. The app switches to
live mode when a key is present; COPILOT_MODE=demo or live forces one or the other.
Use the same MCP server from Claude Code or Claude Desktop:
claude mcp add backoffice -- python -m copilot.mcp_serverDeploy
The Dockerfile runs anywhere that runs containers (Render, Fly.io, a VPS).
Public demo: set
COPILOT_MODE=demo. No API key, no cost. Visitors share one demo database; Reset demo puts the planted problems back.Live: set
ANTHROPIC_API_KEYfrom the host's secret store, set a monthly spending limit on the key in the Claude Console, and adjustCOPILOT_MESSAGES_PER_SESSION(defaultand
COPILOT_MESSAGES_PER_DAY(default 300).
Tech
Python · Claude API (tool use, prompt caching, refusal fallback) · Model Context Protocol (server and client) · FastAPI · Server-Sent Events · SQLite (swap for PostgreSQL in production) · vanilla JavaScript · pytest · evaluation set · GitHub Actions · Docker.
Status and limits
Demo only: in-memory sessions, no login. In production, sign-in (for example Microsoft Entra ID) would identify who approved each action, and only finance roles could approve journals.
The data would come from the real PMS, POS, bank and ERP through the integrations in my other repos (pos-to-ledger, recon-dashboard, m365-mail-intake, marketing-spend-sync).
The recorded answers prove the mechanics and the evaluation harness. Run
python -m copilot.evals --liveto score the real model.All data is fictional.
Built with Claude Code; see CLAUDE.md.
Licence
MIT
This server cannot be deployed
Maintenance
Related MCP Connectors
Your investing research inside Claude: watchlists, financial ratios, reverse-DCF valuations, investment theses, notes, portfolio, earnings calendar and news for the companies you follow. 70 read and write tools; ask what's on your morning desk, rank a watchlist by ROE, log a trade or save a thesis. OAuth sign-in. Free to start.
Read-only finance and operations controls for AI agents with evidence and safe next actions.
- KonnektaOAuthnl.konnekta
Connect Claude to Dutch accounting software: e-Boekhouden invoices, contacts and ledgers.
Connect Claude or Cursor to books, invoices, bills, payroll, and sealed closes.
Related MCP Servers
- AlicenseNot gradedqualityCmaintenanceGives Claude full read/write access to QuickBooks Online, including tools to create, delete, and query accounting data, plus reconciliation features that detect bank-feed gaps and duplicate transactions.MIT
- AlicenseBqualityAmaintenanceEnables AI assistants to query and manage self-hosted accounting data—invoices, balances, and books—through natural language, with read-only tools by default and optional scoped write operations.3048 PyPIAGPL 3.0
- AlicenseNot gradedqualityCmaintenanceEnables LLMs to query a synthetic general ledger and trial balance through read-only tools for account lookups, GL drill-down, variance analysis, and naive forecasting, with every answer cited to specific transactions or periods.24 npmMIT
- AlicenseNot gradedqualityCmaintenanceEnables Claude Desktop users to query recorded business finances—such as revenue, costs, estimated contribution profit, and cash runway—from a paired local Mac app, with in-app approval required for financial reads and separate confirmation before any proposed records are saved.MIT