Skip to content

Commit 1a59896

Browse files
committed
Initial commit: Wave MCP server with 28 tools, auto-refresh, CLI
- 28 MCP tools spanning businesses, invoices (incl. approve/send/mark-sent/payments), customers, estimates, bills, receipts, and products/services - Hybrid REST + GraphQL Wave client (api.waveapps.com + gql.waveapps.com/graphql/internal) - Three auth flows: - `paste` — manual cookie paste (always works) - `login` — puppeteer-extra + stealth (Google OAuth fragile) - `save-password` + auto-refresh — email/password (+ optional TOTP) stored in ~/.config/waveapps-mcp/credentials.json; expired sessions are refreshed invisibly on 401 - 276 tests across 27 files, ~99% line / 98% branch coverage - stdio (default) and streamable-HTTP transports
0 parents  commit 1a59896

74 files changed

Lines changed: 12375 additions & 0 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.env.example

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# Wave session credentials. Copy this file to `.env` and fill in.
2+
#
3+
# How to grab the token (one-time, then again whenever it expires):
4+
# 1. Open https://next.waveapps.com in your browser and log in.
5+
# 2. DevTools → Application → Cookies → next.waveapps.com.
6+
# 3. Copy the value of the `waveapps` cookie into WAVE_AUTH_TOKEN.
7+
# 4. Copy the value of the `identity-csrftoken` cookie into WAVE_CSRF_TOKEN.
8+
#
9+
# The token rotates — if calls start returning 401, re-grab it.
10+
11+
WAVE_AUTH_TOKEN=
12+
WAVE_CSRF_TOKEN=
13+
14+
# Default business UUID. Tools accept a `businessId` argument, but if you
15+
# always work in one workspace you can set it here. Plain UUID, not Relay ID.
16+
WAVE_BUSINESS_ID=
17+
18+
# Transport: "stdio" (default — for Claude Code / Desktop / Cursor) or "http".
19+
WAVE_MCP_TRANSPORT=stdio
20+
21+
# HTTP transport options (ignored when transport=stdio).
22+
WAVE_MCP_PORT=8765
23+
WAVE_MCP_BIND=127.0.0.1

.gitignore

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# ─── Build output / dependencies ─────────────────────────────────────────────
2+
node_modules/
3+
dist/
4+
*.tsbuildinfo
5+
6+
# ─── Test + coverage artefacts ───────────────────────────────────────────────
7+
coverage/
8+
.nyc_output/
9+
10+
# ─── Secrets / environment ───────────────────────────────────────────────────
11+
# `.env.example` is intentionally tracked (template). Real env files are not.
12+
.env
13+
.env.*
14+
!.env.example
15+
16+
# Credentials should live in `~/.config/waveapps-mcp/credentials.json`, NOT here —
17+
# but defensively block anything resembling a credential file from being added.
18+
credentials.json
19+
**/credentials.json
20+
*.pem
21+
*.key
22+
auth.json
23+
secrets.json
24+
25+
# ─── Editor / IDE state ──────────────────────────────────────────────────────
26+
.idea/
27+
.vscode/
28+
*.swp
29+
*.swo
30+
31+
# ─── Claude Code project-local state ─────────────────────────────────────────
32+
.claude/
33+
34+
# ─── npm / pnpm / yarn debug logs ────────────────────────────────────────────
35+
*.log
36+
npm-debug.log*
37+
yarn-debug.log*
38+
yarn-error.log*
39+
pnpm-debug.log*
40+
41+
# ─── OS junk ─────────────────────────────────────────────────────────────────
42+
.DS_Store
43+
Thumbs.db
44+
45+
# ─── Misc local-only ─────────────────────────────────────────────────────────
46+
NOTES.md
47+
*.local

.mcp.json

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"mcpServers": {
3+
"brave-devtools": {
4+
"type": "stdio",
5+
"command": "npx",
6+
"args": [
7+
"-y",
8+
"chrome-devtools-mcp-for-brave@latest",
9+
"--isolated"
10+
],
11+
"env": {}
12+
}
13+
}
14+
}

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2026 Luna Parker
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
# waveapps-mcp
2+
3+
## Purpose
4+
5+
This is an add-on that lets you ask **Claude** (or any other AI assistant that supports MCP) to do your **Wave Accounting** bookkeeping for you. Instead of clicking through the Wave website to draft an invoice, you tell Claude something like:
6+
7+
> "Draft an invoice for Acme Corp — 8 hours of design work at $150/hr."
8+
9+
…and Claude creates the invoice in Wave for you. Same for recording payments, adding customers, looking up old estimates, and so on.
10+
11+
It works by giving Claude a set of structured tools — 23 of them — that map onto real Wave operations. Claude picks the right tools and calls them with the right arguments based on your conversation.
12+
13+
> **What's "MCP"?** [Model Context Protocol](https://modelcontextprotocol.io) — a standard way for AI assistants to call external tools. Anthropic's Claude apps speak it natively; so do Cursor, Continue, and a growing list of others.
14+
15+
## What it can do
16+
17+
Once it's installed, you can ask Claude to:
18+
19+
**Look things up**
20+
- List your Wave workspaces (businesses)
21+
- List or search invoices, customers, estimates, bills, receipts
22+
- List or look up products & services (the line items you put on invoices)
23+
- Pull the full details of a single invoice (line items, payments, taxes, etc.)
24+
- List your chart of accounts
25+
26+
**Create + edit things**
27+
- Add a new customer
28+
- Add, edit, archive, or delete a product/service
29+
- Draft, edit, approve, send, mark-as-sent, or delete an invoice
30+
- Record or remove a payment against an invoice
31+
- Draft, edit, or delete an estimate
32+
33+
Everything happens in your real Wave account — Claude doesn't have its own sandbox. Be deliberate with mutating operations, especially deletes and "send invoice".
34+
35+
## Before you start
36+
37+
You'll need:
38+
39+
- A **Mac, Linux, or Windows computer** with a terminal
40+
- **[Node.js](https://nodejs.org)** version 20 or newer (run `node --version` to check; if you don't have it, install the LTS version from nodejs.org)
41+
- A **[Wave](https://www.waveapps.com)** account you can sign in to in any browser
42+
- An AI client that supports MCP — [Claude Desktop](https://claude.ai/download) is the easiest starting point for non-developers. [Claude Code](https://docs.claude.com/claude-code) works too.
43+
44+
## Installation
45+
46+
In a terminal, somewhere you keep your projects:
47+
48+
```bash
49+
git clone https://github.com/your-username/waveapps-mcp.git
50+
cd waveapps-mcp
51+
npm install
52+
npm run build
53+
```
54+
55+
This downloads the code, installs its dependencies, and compiles it into a `dist/` folder. The compiled entry point is `dist/server.js` — you'll point Claude at that file in a moment.
56+
57+
Take note of the **full absolute path** to that file. You'll need it. On a Mac it might look like `/Users/yourname/code/waveapps-mcp/dist/server.js`. Get it by running:
58+
59+
```bash
60+
echo "$(pwd)/dist/server.js"
61+
```
62+
63+
## Sign in to Wave
64+
65+
The MCP needs to know how to talk to Wave on your behalf. Wave doesn't give regular users an API key, so instead we **borrow your browser's session cookie**. This sounds scary but is the same kind of thing many Wave-related browser extensions do — and the cookie stays on your machine.
66+
67+
```bash
68+
npx waveapps-mcp paste
69+
```
70+
71+
The command prompts you for three values:
72+
73+
1. The `waveapps` cookie value — your session token.
74+
2. The `identity-csrftoken` cookie value — needed when you want Claude to *change* things in Wave (create invoices, record payments, etc.). Skip with Enter if you only want read access.
75+
3. The business UUID — optional; lets Claude default to a specific workspace.
76+
77+
Here's how to get them:
78+
79+
1. Open `https://next.waveapps.com` in any browser. Sign in normally.
80+
2. Open Developer Tools:
81+
- **macOS**: `Cmd + Option + I`
82+
- **Windows / Linux**: `Ctrl + Shift + I`
83+
3. Find the **Application** tab (Chrome/Brave/Edge) or **Storage** tab (Firefox/Safari).
84+
4. In the left sidebar, find **Cookies**`https://next.waveapps.com`.
85+
5. Find `waveapps` in the list and copy its **Value** column. Paste it when the command asks.
86+
6. Repeat for `identity-csrftoken`.
87+
7. For the business UUID, look at the URL in your browser — after sign-in it looks like `next.waveapps.com/c2cb3afe-5a24-41b2-add7-d1c6982d75a9/dashboard/`. The long string in the middle is the UUID. Copy it. (Or skip with Enter and tell Claude which workspace each time.)
88+
89+
The values are saved to `~/.config/waveapps-mcp/credentials.json` with restrictive file permissions (`0600` — only your user can read it).
90+
91+
> **Wave's session expires after a few days.** When that happens, tools will start returning a "WaveAuthError". Just run `npx waveapps-mcp paste` again with fresh cookies. You can also check what's stored at any time with `npx waveapps-mcp whoami` (token values are masked).
92+
93+
### Optional: auto-refresh on expiry
94+
95+
If re-pasting cookies a few times a week sounds annoying, you can opt into having the server log itself back into Wave whenever the session expires. **Read the security caveats first — they're meaningful.**
96+
97+
```bash
98+
npx waveapps-mcp save-password
99+
```
100+
101+
The command prompts for:
102+
103+
1. **Your Wave email** (defaults to the previous value if you've run it before).
104+
2. **Your Wave password** (input hidden).
105+
3. **Whether you have 2FA enabled.** If yes, it asks for your **TOTP secret** (the base32 string from your authenticator-app setup screen). You can usually re-export this from your authenticator app's "show secret" / "export" menu. If you don't have it, set up 2FA again temporarily and copy the secret when the QR code shows.
106+
107+
Once stored, the server uses a headless browser to silently re-log-in against Wave's own username/password form (no Google in the path, so no "browser may not be secure" issue) whenever a tool call returns 401. The retry happens transparently — the model just sees the original call succeed after ~5-10 seconds.
108+
109+
#### ⚠ Security trade-offs
110+
111+
- **Plaintext password.** Stored at `~/.config/waveapps-mcp/credentials.json` with mode 0600 (only your user can read it). Any process running as you (sketchy extensions, malware) can lift it. The trade-off is convenience for risk.
112+
- **Storing the TOTP secret defeats 2FA.** Two-factor auth's whole point is that the second factor lives somewhere other than the first factor. Putting both in the same file collapses that protection. Only do this if your threat model already accepts machine compromise (single-user dev machine, FileVault/full-disk encryption on, no shared access).
113+
- **Wave may flag the IP / send security emails** if it sees the same account log in from a headless browser at unusual times. Heads-up — you may get a "new sign-in" notification.
114+
115+
To remove the auto-refresh later: re-run `save-password` and leave email/password blank, OR `npx waveapps-mcp logout` to wipe everything and start over with `paste`.
116+
117+
## Wire it into Claude Desktop
118+
119+
Find your config file:
120+
121+
- **macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json`
122+
- **Windows**: `%APPDATA%\Claude\claude_desktop_config.json`
123+
124+
If the file doesn't exist yet, create it. Open it in any text editor and add a `waveapps` entry under `mcpServers`. If you already have other MCP servers configured, just add `waveapps` to the existing list — don't replace the whole file.
125+
126+
```json
127+
{
128+
"mcpServers": {
129+
"waveapps": {
130+
"command": "node",
131+
"args": ["/absolute/path/to/waveapps-mcp/dist/server.js"]
132+
}
133+
}
134+
}
135+
```
136+
137+
Replace `/absolute/path/to/waveapps-mcp/dist/server.js` with the path from earlier.
138+
139+
Quit Claude Desktop and reopen it. You should see a small 🔌 icon in the chat input — clicking it lists the available tools. If you see `wave_list_businesses`, `wave_create_invoice`, etc., it's wired up.
140+
141+
## Wire it into Claude Code
142+
143+
Run this in any project directory (or in your home directory for a global setup):
144+
145+
```bash
146+
claude mcp add waveapps -- node /absolute/path/to/waveapps-mcp/dist/server.js
147+
```
148+
149+
Then start a Claude Code session. The Wave tools become available automatically.
150+
151+
## Try it
152+
153+
Once wired up, try asking Claude:
154+
155+
- *"Show me my Wave businesses."*
156+
- *"List my draft invoices."*
157+
- *"Who's my most overdue customer?"*
158+
- *"Draft an invoice for [customer name] — 6 hours of consulting at $200/hr, dated today."*
159+
- *"Record a $500 cash payment against invoice #218."*
160+
161+
Claude will figure out which tools to call. If it doesn't have enough info (e.g. you didn't say which customer), it'll ask.
162+
163+
## When something goes wrong
164+
165+
| Symptom | Fix |
166+
| --- | --- |
167+
| Claude says "I don't have a tool for that" | Restart your Claude app — it only picks up MCP servers at startup. Confirm the path in your config points at a real file. |
168+
| Tools error with "WaveAuthError" | Wave session expired. Run `npx waveapps-mcp paste` with fresh cookies — or set up `save-password` for invisible auto-refresh. |
169+
| Tools error with "CSRF token configured" | You skipped the CSRF cookie when pasting. Re-run `npx waveapps-mcp paste` and provide it this time. |
170+
| Claude tries to use a tool but says it can't find your business | Either pass `businessId` explicitly in your message, or re-run `paste` and provide the business UUID. |
171+
| Auto-refresh fires but still fails | Check Wave's email for "new sign-in" notifications. Common causes: password changed, 2FA enabled (run `save-password` again with the TOTP secret), or Wave is rate-limiting from your IP. |
172+
| Want to wipe everything | `npx waveapps-mcp logout` removes the credentials file. |
173+
174+
## Important caveats
175+
176+
- **This uses Wave's internal API, not a public/documented one.** Wave doesn't promise to keep its internal endpoints stable. If something breaks one day, it's probably because Wave changed something on their end — open an issue and we'll patch it.
177+
- **Treat your credentials file as a password.** Anyone with read access to `~/.config/waveapps-mcp/credentials.json` can act as you in Wave. Don't commit it to git, don't paste it into shared chats, don't sync it to a public folder.
178+
- **Be careful with destructive tools.** `wave_delete_invoice`, `wave_delete_customer`, `wave_send_invoice` etc. take real action against your live workspace. Claude usually confirms before destructive operations, but double-check what it's about to do.
179+
- **Vendors aren't wrapped yet.** They live on Wave's older `accounting.waveapps.com` frontend. If you need to bill a new vendor on a Wave Bill, you'll have to add them through Wave's website first.
180+
181+
## License
182+
183+
MIT — see [LICENSE](./LICENSE).
184+
185+
You can use, modify, and redistribute this freely. There's no warranty; if it breaks your invoicing workflow that's on you. (Read: keep eyes on what Claude is doing in your Wave account, especially for the first few days.)
186+
187+
## For developers / contributors
188+
189+
Brief pointers if you want to hack on this:
190+
191+
- Source layout, API map, and captured GraphQL operations: see [docs/api-map.md](./docs/api-map.md) and [docs/captured-queries.md](./docs/captured-queries.md)
192+
- Run the test suite: `npm test` (or `npm run test:coverage` for an HTML coverage report in `coverage/`)
193+
- Smoke-test against a live Wave account: `WAVE_AUTH_TOKEN=... npm run smoke`
194+
- An experimental browser-driven login flow is available via `npx waveapps-mcp login` — it uses puppeteer-core + the stealth plugin to try to get past Google's OAuth check. Often blocked; the manual `paste` flow above is more reliable.
195+
196+
Pull requests welcome — especially for Products & Services, Vendors, recurring invoices, and bill mutations, which are the main unwrapped surfaces.

0 commit comments

Comments
 (0)