Skip to content

Repository files navigation


Glassbook

Your money. Your browser. Your agent.

Live Stack Challenge License

What this is

Glassbook is a budgeting app that stays in your browser. You import a statement, sort spend, set budgets, and teach a few rules. None of that leaves this tab. There is no account and no server.

The usual deal with "AI budgeting" is the other way around. You upload a CSV, connect Plaid, or paste a statement into a chat so a model can help. I built this because I will not send a year of swipes to someone else's server so a model can lecture me about dining.

Instead, the agent comes here. WebMCP tools run in the page, against the same localStorage ledger you see. Every call shows up in the activity log. If a write goes wrong, you can undo the last one or undo everything the agent did.

Once you connect an agent, tool results can enter its context. Tools still send summaries and cleaned merchant names, not your whole statement, unless you ask. You can see what went out. You can rewind what got written.

Why WebMCP

People already refuse to paste a bank statement into a chat box. They should not have to upload one to a SaaS either.

WebMCP is what lets an agent work on data the app never uploads. The tools live in this page. The page decides what they can read, how little they return, and how undo works. There is no remote MCP server sitting on a copy of the ledger.

The screen is shared too. The agent can open Transactions, filter to Dining, and ring the Delta row while it talks about June. A server-side tool cannot point at a row on your screen.

Load the demo and you get about 320 uncategorized rows (May through August 2026). Clicking chips by hand takes forever, and it is easy to miss Scribd at $11.99 or that June 18 was Delta and Airbnb. Ask the agent to categorize everything, then make a rule so a correction sticks. After that, look for forgotten subscriptions, ask why June was expensive, and set a dining budget. If the streak was too aggressive, Undo all agent changes walks it back.

What's in the app

  • Dashboard, transactions, budgets, import, and rules. The demo is a fixed four-month sample. June 18 is the trip (Delta −$420, Airbnb −$380).
  • Import stays in this tab. CSV and TSV go through PapaParse. OFX / QFX have a local parser. Excel and text PDFs go through AnyDoc WASM in a worker. Scanned PDFs fail here. Nothing is uploaded.
  • The agent can categorize many rows in one go. Chips update on screen when that happens.
  • Correct one merchant, save a rule, and optionally apply it to the other uncategorized matches.
  • The agent can change views, set filters, highlight rows (clears after 10 seconds), and pin a note on the dashboard.
  • The activity log records every tool call. Undo last and Undo all agent changes sit at the top.
  • Appearance follows your system unless you pick light or dark.

Try it

No login. Data stays in this browser and is still there after a reload.

Live: glassbook.vercel.app

  1. Open Import and click Load demo. If you already have rows, confirm replace. You should land on Transactions with about 320 uncategorized rows.

  2. Connect an agent:

    • ChatGPT desktop (easiest). Open the live URL in the built-in browser, not a Codex preview. Use GPT-5.6 Sol or GPT-5.6 Terra. Site tools are off on Luna and Light. Check Site tools in the address bar. The sidebar should say 18 tools live.
    • Chrome 149+. Turn on chrome://flags/#enable-webmcp-testing, relaunch, then open the live URL.
  3. Click Connect agent for copy-paste prompts. Open Activity log. Then ask, in this order:

    • Categorize all my transactions
    • Make a rule so that sticks (fix one chip by hand first)
    • Find subscriptions I might have forgotten about (look for Scribd at $11.99)
    • Why was June so expensive? (June 18: Delta −$420, Airbnb −$380)
    • Set a $300 monthly dining budget
  4. In the activity log, click Undo all agent changes. Categories, rules, and that dining budget should rewind.

No WebMCP host?

Open DevTools on the live URL. The page always installs window.__glassbookTools:

window.__glassbookTools.list() // 18 tools
await window.__glassbookTools.execute("get_overview", {})
await window.__glassbookTools.execute("list_transactions", { month: "2026-06", limit: 20 })

That path uses the same wrapper as a live agent: activity entries, Error: strings, and MCP text results.

How WebMCP is wired up

After the store loads, tools register on document.modelContext, with fallbacks to navigator.modelContext and window.modelContext. Each tool is defined once in src/webmcp/tools/ with defineTool, then registered from src/webmcp/register.ts. If the host injects modelContext after the first paint, registration retries.

await document.modelContext.registerTool({
  name: "get_overview",
  description: "Call this first to understand the user's ledger.",
  inputSchema: { type: "object", properties: {}, additionalProperties: false },
  annotations: { readOnlyHint: true },
  execute: async (input) => {
    const text = await executeRegisteredTool("get_overview", input);
    return { content: [{ type: "text", text }] };
  },
});

All 18 tools use that shape: name, description, inputSchema, execute. Read tools set readOnlyHint: true. One AbortController is shared across registerTool(..., { signal }). Re-init cancels the previous set.

Results go back as MCP { content: [{ type: "text", text }] }. Handlers do not throw to the host. A failure is an Error: … string so the model can try again (unknown category, bad month, missing id). Amounts in the tool API are dollars, and expenses are negative. Categories accept an id or a name. "uncategorized" is a valid filter.

executeRegisteredTool logs every call as the agent. A write saves a snapshot of the ledger (up to 50). undoLast pops one snapshot. undoAll walks back a run of agent writes. Reads are logged and do not create undo snapshots.

What tools return on purpose:

  • get_spending_summary sends grouped totals, not hundreds of rows.
  • list_transactions sends cleaned merchant names, 50 by default and 100 at most, and never rawDescription.
  • find_recurring figures cadence locally and returns a short list (merchant, amount, cadence, yearly cost).
  • There are no account numbers or balances in the data or in tool output.

18 tools

Name Type What it does
get_overview read First look: months, totals, categories, budgets, rules, current view.
list_transactions read Page of ids and cleaned merchants. Filter by month, category, or search.
get_spending_summary read Expenses grouped by category, merchant, or month.
find_recurring read Subscriptions and rent: 3+ hits, similar amounts, weekly or monthly.
get_budget_status read Limit, spent, remaining, and over/under for a month.
get_dashboard_state read What is on screen: view, month, filters, highlights, pinned titles.
categorize_transactions write Categorize many rows at once. One undo for the whole batch.
create_category write Add a category when the defaults are not enough.
create_rule write Substring rule on merchant or description. Can apply it now.
apply_rules write Run saved rules on the remaining uncategorized rows.
set_budget write Set or clear a monthly category limit (null / 0 clears it).
add_transaction write Add one cash or correction row.
delete_transactions write Delete rows by id. One undo for the batch.
annotate_transaction write Set or clear a note without changing the category.
pin_insight write Pin a finding on the dashboard so it stays.
highlight_transactions ui Ring rows, scroll the first into view, clear after 10 seconds.
set_view_filters ui Set the transaction filters and open that view.
navigate_to_view ui Switch dashboard, transactions, budgets, import, or rules.

Run from source

git clone https://github.com/BlinkZ404/GlassBook.git
cd GlassBook
npm install
npm run dev
npm test
npm run build

Vite, React 19, TypeScript, Tailwind, and Zustand. Node 20+. No extra services to stand up.

Privacy

  • The ledger is stored in localStorage under glassbook-store. The app never sends it to a server.
  • Import (CSV, Excel, OFX, PDF) runs in this tab. Spreadsheet and PDF conversion uses AnyDoc WASM in a worker. Scanned PDFs are rejected here. There is no remote OCR.
  • Once you connect an agent, tool results can enter its context. Those calls are logged. Writes can be rewound.
  • No accounts, analytics, or telemetry.

Project structure

src/
├── App.tsx                 # Hydrate store, init WebMCP, route views
├── components/             # Shell, sidebar, activity log, import UI
├── views/                  # Dashboard, transactions, budgets, import, rules
├── store/                  # Zustand persist + undo snapshots
├── lib/
│   ├── csv.ts              # PapaParse + column mapping
│   ├── import/             # OFX, Excel, PDF / AnyDoc worker
│   ├── activity.ts         # Log copy and icons
│   └── rules.ts            # Merchant substring rules
└── webmcp/
    ├── register.ts         # document.modelContext.registerTool
    ├── registry.ts         # defineTool
    ├── wrapper.ts          # Activity + undo around every call
    └── tools/              # read.ts, write.ts, ui.ts
public/
├── logo.png
├── favicon.ico
├── favicon-16.png
├── favicon-32.png
├── apple-touch-icon.png
├── icon-512.png
├── og.png                  # 16:9 social
└── devpost-glassbook.png   # 3:2 gallery
LICENSE                     # MIT

License

MIT. See LICENSE. © 2026 Arifur Rahman.

Built for the OpenAI WebMCP Challenge.

Author Repo

About

Your money. Your browser. Your agent. Local-first budgeting. The agent works in the page.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages