@oliverames/mcp-server-for-wave 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,23 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Oliver Ames
4
+
5
+ Portions derived from wave_mcp, Copyright (c) 2025 vinnividivicci.
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to deal
9
+ in the Software without restriction, including without limitation the rights
10
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in all
15
+ copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,434 @@
1
+ <p align="center">
2
+ <img src="assets/icon.png" width="80" height="80" alt="Wave MCP Server">
3
+ </p>
4
+
5
+ <h1 align="center">MCP Server for Wave</h1>
6
+
7
+ <p align="center">
8
+ <strong>Complete access to Wave Accounting from Claude, Codex, and any other MCP client</strong>
9
+ </p>
10
+
11
+ <p align="center">
12
+ <code>74 tools</code> &bull;
13
+ <code>42/42 mutations</code> &bull;
14
+ <code>11/11 queries</code> &bull;
15
+ <code>7 resources</code>
16
+ </p>
17
+
18
+ <p align="center">
19
+ <a href="https://www.npmjs.com/package/@oliverames/mcp-server-for-wave">
20
+ <img src="https://img.shields.io/npm/v/@oliverames/mcp-server-for-wave?style=flat-square&color=f5a542" alt="npm">
21
+ </a>
22
+ <a href="LICENSE">
23
+ <img src="https://img.shields.io/badge/license-MIT-f5a542?style=flat-square" alt="License">
24
+ </a>
25
+ <a href="https://www.buymeacoffee.com/oliverames">
26
+ <img src="https://img.shields.io/badge/Buy_Me_a_Coffee-support-f5a542?style=flat-square&logo=buy-me-a-coffee&logoColor=white" alt="Buy Me a Coffee">
27
+ </a>
28
+ </p>
29
+
30
+ ---
31
+
32
+ Wave gives small businesses free accounting and invoicing, and a GraphQL API that
33
+ covers nearly all of it. This server puts that entire API in front of an AI
34
+ assistant: invoices and payments, estimates and deposits, customers, vendors,
35
+ products, sales taxes, the chart of accounts, and double-entry bookkeeping.
36
+
37
+ Every query is verified against Wave's live schema in CI, and the tools that
38
+ change or send anything stay hidden until you turn them on.
39
+
40
+ ## Why This Exists
41
+
42
+ Bookkeeping is mostly translation. You have a receipt, a bank line, an email
43
+ promising to pay next week, and none of it is in the shape your books want. The
44
+ work is not hard, it is just constant, and it is exactly the kind of task worth
45
+ handing to an assistant that can hold the whole picture at once.
46
+
47
+ Doing that well needs more than a few convenience endpoints. An assistant that
48
+ can list invoices but not record the payment, or draft an estimate but not
49
+ convert it, forces you back into the web app halfway through every task. So this
50
+ server covers the API completely: all 42 mutations, all 11 root queries, every
51
+ sub-resource on a business. If Wave's API can do it, a tool here does it.
52
+
53
+ Two decisions shape the rest:
54
+
55
+ **Writes are off by default.** Wave has genuinely irreversible operations.
56
+ Sending an invoice emails a real customer. Deleting one is permanent. A default
57
+ install exposes 30 read-only tools; the other 44 appear only when you set
58
+ `WAVE_ALLOW_WRITES=1`. Reading your books should not require trusting a model
59
+ with your outbox.
60
+
61
+ **Errors explain themselves.** Wave rejects an unbalanced transaction without
62
+ telling you which figure is wrong. This server compares the anchor against the
63
+ line items first and reports the difference. A category word that matches no
64
+ account produces the list of real account names rather than a silent guess at
65
+ the first one.
66
+
67
+ ## Quick Start
68
+
69
+ ```json
70
+ {
71
+ "mcpServers": {
72
+ "wave-mcp-server": {
73
+ "command": "npx",
74
+ "args": ["-y", "@oliverames/mcp-server-for-wave@latest"],
75
+ "env": {
76
+ "WAVE_ACCESS_TOKEN": "your_token_here"
77
+ }
78
+ }
79
+ }
80
+ }
81
+ ```
82
+
83
+ Then ask for your businesses and set one as the default:
84
+
85
+ ```
86
+ List my Wave businesses and set the first one as the default.
87
+ ```
88
+
89
+ ### Get a token
90
+
91
+ Create an application and generate an access token in the
92
+ [Wave developer portal](https://developer.waveapps.com/). Wave's tokens expire,
93
+ so expect to refresh it periodically, or use the
94
+ [hosted connector](#hosted-connector) which handles refresh for you.
95
+
96
+ ### Install as a plugin
97
+
98
+ The repo doubles as a single-plugin marketplace for hosts that support them:
99
+
100
+ ```bash
101
+ /plugin marketplace add oliverames/wave-mcp-server
102
+ /plugin install wave-mcp-server
103
+ ```
104
+
105
+ ### Install in Codex
106
+
107
+ ```bash
108
+ codex mcp add wave-mcp-server \
109
+ --env WAVE_ACCESS_TOKEN=your_token_here \
110
+ -- npx -y @oliverames/mcp-server-for-wave@latest
111
+ ```
112
+
113
+ Verify with `codex mcp list`. Startup takes about 0.2s, well inside Codex's
114
+ 10-second `startup_timeout_sec`, and the retry budget is capped below its
115
+ 60-second `tool_timeout_sec` so a slow API surfaces Wave's real error rather
116
+ than a client timeout.
117
+
118
+ ### Enable write tools
119
+
120
+ ```json
121
+ "env": {
122
+ "WAVE_ACCESS_TOKEN": "your_token_here",
123
+ "WAVE_ALLOW_WRITES": "1"
124
+ }
125
+ ```
126
+
127
+ This registers the 44 tools that create, change, delete, or email records.
128
+ Without it they are not advertised at all, so a model cannot call one by
129
+ guessing its name.
130
+
131
+ ### Docker
132
+
133
+ ```bash
134
+ docker build -t wave-mcp-server .
135
+ docker run --rm -i -e WAVE_ACCESS_TOKEN=your_token_here wave-mcp-server
136
+ ```
137
+
138
+ The `-i` matters: the server speaks JSON-RPC on stdin and stdout.
139
+
140
+ ### 1Password token lookup
141
+
142
+ Rather than pasting a token into a config file, point the server at a secret
143
+ reference and it will shell out to the `op` CLI on startup:
144
+
145
+ ```json
146
+ "env": { "WAVE_OP_PATH": "op://Development/Wave/credential" }
147
+ ```
148
+
149
+ `WAVE_ACCESS_TOKEN_FILE` works the same way for a file on disk.
150
+
151
+ ## What You Can Do
152
+
153
+ **Bill a customer end to end**
154
+
155
+ ```
156
+ Create an invoice for Acme Corp with 10 hours of consulting at $150/hour,
157
+ due in 30 days. Approve it and email it to billing@acme.com.
158
+ ```
159
+
160
+ **Quote, then convert**
161
+
162
+ ```
163
+ Create an estimate for the website redesign package with a 25% deposit,
164
+ send it, and convert it to an invoice once they accept.
165
+ ```
166
+
167
+ **Record a receipt**
168
+
169
+ ```
170
+ Log a $45.99 expense from Office Depot on 2026-03-15 for office supplies,
171
+ paid from Business Checking.
172
+ ```
173
+
174
+ **Split a transaction**
175
+
176
+ ```
177
+ Record a $100 withdrawal from checking: $60 to fuel and $40 to meals.
178
+ ```
179
+
180
+ **Reconcile a processor payout**
181
+
182
+ ```
183
+ A Stripe payout of $97 landed in checking: $100 of consulting income
184
+ less a $3 processing fee.
185
+ ```
186
+
187
+ **Chase what is owed**
188
+
189
+ ```
190
+ Show me every unpaid invoice over $500, sorted by amount due, and which
191
+ customers carry the largest overdue balances.
192
+ ```
193
+
194
+ ## Tools Reference
195
+
196
+ Names are prefixed `wave_` so they do not collide with other MCP servers.
197
+ Tools marked **W** require `WAVE_ALLOW_WRITES=1`.
198
+
199
+ ### Businesses and reference data
200
+
201
+ | Tool | Purpose |
202
+ |------|---------|
203
+ | `wave_list_businesses` | List reachable businesses |
204
+ | `wave_get_business` | Full business detail |
205
+ | `wave_set_default_business` | Set the session default |
206
+ | `wave_get_invoice_estimate_settings` | Accent color and logo |
207
+ | `wave_auth_status` | How credentials resolved, and what is gated. Makes no API call |
208
+ | `wave_get_user` | Account the token belongs to |
209
+ | `wave_get_oauth_application` | Application that issued the token |
210
+ | `wave_list_currencies` / `wave_get_currency` | Supported currencies |
211
+ | `wave_list_countries` / `wave_get_country` | Countries and their provinces |
212
+ | `wave_get_province` | One province or state |
213
+ | `wave_list_account_types` | The five top-level account types |
214
+ | `wave_list_account_subtypes` | Subtypes, which `wave_create_account` requires |
215
+
216
+ ### Chart of accounts
217
+
218
+ | Tool | Purpose |
219
+ |------|---------|
220
+ | `wave_list_accounts` / `wave_get_account` | Accounts with balances |
221
+ | `wave_create_account` **W** | Add an account |
222
+ | `wave_patch_account` **W** | Rename or renumber |
223
+ | `wave_archive_account` **W** | Hide from pickers, keep history |
224
+
225
+ ### Customers, vendors, products, taxes
226
+
227
+ | Tool | Purpose |
228
+ |------|---------|
229
+ | `wave_list_customers` / `wave_get_customer` | Customers with balances |
230
+ | `wave_create_customer` / `wave_patch_customer` / `wave_delete_customer` **W** | Manage customers |
231
+ | `wave_list_vendors` / `wave_get_vendor` | Vendors (read-only in Wave's API) |
232
+ | `wave_list_products` / `wave_get_product` | Products and services |
233
+ | `wave_create_product` / `wave_patch_product` / `wave_archive_product` **W** | Manage products |
234
+ | `wave_list_sales_taxes` / `wave_get_sales_tax` | Taxes and rate history |
235
+ | `wave_create_sales_tax` / `wave_patch_sales_tax` / `wave_archive_sales_tax` **W** | Manage taxes |
236
+
237
+ ### Invoices and payments
238
+
239
+ | Tool | Purpose |
240
+ |------|---------|
241
+ | `wave_list_invoices` / `wave_get_invoice` | Invoices with items and payments |
242
+ | `wave_create_invoice` / `wave_patch_invoice` / `wave_clone_invoice` **W** | Build invoices |
243
+ | `wave_approve_invoice` **W** | Move a draft into the books |
244
+ | `wave_send_invoice` **W** | Emails the customer |
245
+ | `wave_mark_invoice_sent` **W** | Record delivery made outside Wave |
246
+ | `wave_delete_invoice` **W** | Permanent |
247
+ | `wave_get_invoice_payment` | One payment |
248
+ | `wave_create_invoice_payment` / `wave_patch_invoice_payment` / `wave_delete_invoice_payment` **W** | Record payments |
249
+ | `wave_send_invoice_payment_receipt` **W** | Emails the customer |
250
+
251
+ ### Estimates and deposits
252
+
253
+ | Tool | Purpose |
254
+ |------|---------|
255
+ | `wave_list_estimates` / `wave_get_estimate` | Estimates with history and deposits |
256
+ | `wave_create_estimate` / `wave_patch_estimate` / `wave_clone_estimate` **W** | Build estimates |
257
+ | `wave_approve_estimate` **W** | Approve a draft |
258
+ | `wave_send_estimate` **W** | Emails the customer |
259
+ | `wave_mark_estimate_sent` / `wave_mark_estimate_accepted` **W** | Record offline delivery and acceptance |
260
+ | `wave_reset_estimate_acceptance` **W** | Undo an acceptance |
261
+ | `wave_send_estimate_acceptance_email` **W** | Emails the customer |
262
+ | `wave_generate_estimate_pdf` **W** | Render a PDF |
263
+ | `wave_convert_estimate_to_invoice` **W** | Turn an accepted estimate into an invoice |
264
+ | `wave_delete_estimate` **W** | Permanent |
265
+ | `wave_get_estimate_payment` | One deposit payment |
266
+ | `wave_create_estimate_deposit_payment` / `wave_update_estimate_deposit_payment` / `wave_delete_estimate_payment` **W** | Record deposits |
267
+ | `wave_send_estimate_deposit_receipt` **W** | Emails the customer |
268
+
269
+ ### Bookkeeping
270
+
271
+ | Tool | Purpose |
272
+ |------|---------|
273
+ | `wave_create_money_transaction` **W** | One expense, income, or transfer |
274
+ | `wave_create_money_transactions` **W** | Bulk import, applied atomically |
275
+ | `wave_create_deposit_transaction` **W** | A payout whose net differs from gross |
276
+ | `wave_create_expense_from_receipt` **W** | Expense, account matched from a category word |
277
+ | `wave_create_income_from_payment` **W** | Income, account matched from a category word |
278
+
279
+ ## Resources
280
+
281
+ Read-only JSON views for grounding context. Everything here is also reachable
282
+ through a tool, so hosts that ignore resources lose nothing.
283
+
284
+ `wave://businesses` &bull; `wave://accounts` &bull; `wave://customers` &bull;
285
+ `wave://vendors` &bull; `wave://products` &bull; `wave://sales-taxes` &bull;
286
+ `wave://account-taxonomy`
287
+
288
+ ## How Transactions Work
289
+
290
+ Wave is double-entry, so `wave_create_money_transaction` has two sides:
291
+
292
+ - The **anchor** is the account money physically moved through, a bank account
293
+ or credit card, with a direction of `DEPOSIT` or `WITHDRAWAL`.
294
+ - The **line items** are the categories it is attributed to. Their amounts must
295
+ total the anchor amount.
296
+
297
+ A $50 office-supplies expense paid from checking is one anchor (checking,
298
+ `WITHDRAWAL`, `50.00`) and one line item (Office Supplies, `50.00`). A split is
299
+ the same anchor with more line items.
300
+
301
+ Every transaction carries an `external_id`. Wave deduplicates on it, so passing
302
+ a stable value of your own makes retries safe; one is generated when you omit
303
+ it.
304
+
305
+ ## Environment Variables
306
+
307
+ | Variable | Required | Default | Description |
308
+ |----------|----------|---------|-------------|
309
+ | `WAVE_ACCESS_TOKEN` | Yes | (none) | OAuth2 bearer token from the Wave developer portal |
310
+ | `WAVE_BUSINESS_ID` | No | (none) | Default business, so tools can omit `business_id` |
311
+ | `WAVE_ALLOW_WRITES` | No | off | Set to `1` to register the 44 tools that change or send data |
312
+ | `WAVE_ACCESS_TOKEN_FILE` | No | (none) | Read the token from a file instead |
313
+ | `WAVE_OP_PATH` | No | (none) | Read the token from 1Password, e.g. `op://Vault/Item/credential` |
314
+ | `WAVE_TIMEOUT_MS` | No | `20000` | Per-request timeout |
315
+ | `WAVE_TOTAL_BUDGET_MS` | No | `50000` | Total time for one call including retries |
316
+ | `WAVE_MAX_RESPONSE_BYTES` | No | `8388608` | Reject responses above this size |
317
+ | `WAVE_HTTP_RETRIES` | No | `2` | Retries on 429 and 5xx |
318
+ | `WAVE_DISABLE_AGENT_CONFIG_FALLBACK` | No | off | Read only the environment, not agent config files |
319
+
320
+ Credentials resolve in order: environment, then the host agent's own config
321
+ file, then `WAVE_ACCESS_TOKEN_FILE`, then 1Password. Reading the agent config
322
+ matters because MCP clients launch this server as a subprocess, so a value in
323
+ `claude_desktop_config.json` or `~/.codex/config.toml` reaches it only if the
324
+ user wired it through by hand.
325
+
326
+ ## Amount Handling
327
+
328
+ Money is sent to Wave as strings, not floats, so `0.1 + 0.2` cannot become
329
+ `0.30000000000000004` on the way to your ledger. Balance checks compare minor
330
+ units as integers for the same reason.
331
+
332
+ One exception, and it is Wave's: `moneyDepositTransactionCreate` types its
333
+ amounts as `Float` rather than `Decimal`, so `wave_create_deposit_transaction`
334
+ sends numbers there because the API accepts nothing else.
335
+
336
+ ## Wave API Limitations
337
+
338
+ These are constraints in Wave's API, not gaps here. Each was confirmed against
339
+ the live schema.
340
+
341
+ - **Transactions cannot be read back.** Wave creates money transactions but
342
+ exposes no query to list them; there is no `transactions` connection on
343
+ `Business`. Review them in the web app.
344
+ - **Vendors are read-only.** The schema has no `vendorCreate`, `vendorPatch`,
345
+ or `vendorDelete`.
346
+ - **Money transactions cannot reference a vendor.**
347
+ `wave_create_expense_from_receipt` records the name in the description.
348
+ - **`wave_patch_estimate` demands fields you are not changing.** Wave marks
349
+ seven of them required on the patch input; read the estimate first and pass
350
+ its current values back.
351
+ - **`wave_patch_account` needs the account's current `sequence`** as an
352
+ optimistic-concurrency check.
353
+ - **Line items must reference a product.** No free-text lines.
354
+ - **`wave_create_deposit_transaction` returns no ID.**
355
+ - **Bills, receipts, payroll, and reports have no API.**
356
+ - **No file attachments.** Receipt images cannot be uploaded.
357
+ - **Rate limits are tight**, roughly two concurrent requests.
358
+ - **Un-archiving is web-app only.**
359
+
360
+ ## Hosted Connector
361
+
362
+ `worker/` holds a Cloudflare Worker serving the same tools at a URL, with
363
+ per-user OAuth instead of a shared token. Users authorize against their own
364
+ Wave account, tokens are encrypted before storage, and write access is chosen
365
+ at authorization time so a read-only connection cannot be escalated later.
366
+
367
+ See [worker/README.md](worker/README.md) for setup and the security model.
368
+
369
+ ## Architecture
370
+
371
+ ```
372
+ index.js Single-file server: client, 74 tools, 7 resources
373
+ createWaveServer() Factory over injected credentials, shared by
374
+ the stdio process and the hosted Worker
375
+ scripts/
376
+ smoke-validate-graphql.mjs Schema-check every query against live Wave
377
+ smoke-list-tools.mjs Start over stdio and enumerate what is advertised
378
+ sync-plugin-metadata.mjs Propagate the version to every host manifest
379
+ check-release-consistency Fail the build when anything disagrees
380
+ build-mcpb.mjs Desktop bundle
381
+ worker/ Hosted OAuth connector
382
+ test/unit.test.mjs 54 tests, no network
383
+ ```
384
+
385
+ The tool layer lives in one file on purpose. It is imported unchanged by the
386
+ Worker, so the hosted and local servers cannot drift apart.
387
+
388
+ ### Verification without credentials
389
+
390
+ Wave validates a GraphQL document and coerces its variables *before* it checks
391
+ authentication. An `UNAUTHENTICATED` response therefore means the query is
392
+ correct, while `GRAPHQL_VALIDATION_FAILED` means it is not.
393
+
394
+ CI exploits that to schema-check all 64 documents on every push with no token
395
+ at all, which catches a field Wave renames before a user does.
396
+
397
+ ## Building
398
+
399
+ ```bash
400
+ npm install
401
+ npm test # 54 unit tests, no network
402
+ npm run smoke:list-tools # start over stdio, enumerate tools
403
+ npm run smoke:schema # validate every query against live Wave
404
+ npm run release:check # version parity across 8 manifests
405
+ npm run build:mcpb # desktop bundle
406
+ ```
407
+
408
+ Contributions welcome. See [CONTRIBUTING.md](CONTRIBUTING.md).
409
+
410
+ ## Not Affiliated With Wave
411
+
412
+ An independent project, not affiliated with, endorsed by, or sponsored by Wave
413
+ Financial Inc. Wave Financial Inc. owns the Wave name, logo, and marks; the icon
414
+ above is theirs and is used only to identify the service this server connects
415
+ to. Originally forked from
416
+ [vinnividivicci/wave_mcp](https://github.com/vinnividivicci/wave_mcp), then
417
+ rewritten.
418
+
419
+ ---
420
+
421
+ <p align="center">
422
+ <a href="https://www.buymeacoffee.com/oliverames">
423
+ <img src="https://img.shields.io/badge/Buy_Me_a_Coffee-support-f5a542?style=for-the-badge&logo=buy-me-a-coffee&logoColor=white" alt="Buy Me a Coffee">
424
+ </a>
425
+ </p>
426
+
427
+ <p align="center">
428
+ <sub>
429
+ Built by <a href="https://ames.consulting">Oliver Ames</a> in Vermont
430
+ &bull; <a href="https://github.com/oliverames">GitHub</a>
431
+ &bull; <a href="https://linkedin.com/in/oliverames">LinkedIn</a>
432
+ &bull; <a href="https://bsky.app/profile/oliverames.bsky.social">Bluesky</a>
433
+ </sub>
434
+ </p>
Binary file
@@ -0,0 +1,54 @@
1
+ # Hosted OAuth connector
2
+
3
+ Most MCP clients can connect to a remote server over HTTP instead of launching
4
+ a local process. That path suits Wave well: rather than each user minting an
5
+ access token in the developer portal and pasting it into a config file, they
6
+ authorize the connector against their own Wave account and it handles refresh.
7
+
8
+ The implementation lives in [`worker/`](../worker/README.md); this document
9
+ covers what it is for and how to reason about it.
10
+
11
+ ## When to use which
12
+
13
+ | | Local stdio | Hosted connector |
14
+ |---|---|---|
15
+ | Setup | Paste a token into your client config | Click through an OAuth screen |
16
+ | Token refresh | You regenerate it when it expires | Automatic |
17
+ | Where credentials live | Your machine | Encrypted in Cloudflare KV |
18
+ | Works offline against Wave | No, Wave is remote either way | No |
19
+ | Multi-user | One token per install | One authorization per user |
20
+ | Best for | A single developer on one machine | Sharing with a team, or a client that only speaks HTTP |
21
+
22
+ If you are the only user and you are comfortable managing a token, the local
23
+ server is simpler and has a smaller surface. The hosted connector earns its
24
+ complexity when more than one person needs access, or when your client cannot
25
+ launch a subprocess.
26
+
27
+ ## Access levels
28
+
29
+ Write access is chosen during authorization, not afterwards. A read-only
30
+ connection requests only Wave's read scopes, so it is not merely that the
31
+ server declines to offer write tools: Wave never granted the permission, and no
32
+ later prompt injection can escalate it.
33
+
34
+ Choose write access only for a client you trust to act on your behalf. Sending
35
+ an invoice emails a real customer.
36
+
37
+ ## Endpoints
38
+
39
+ | Path | Purpose |
40
+ |------|---------|
41
+ | `/mcp` | Streamable HTTP transport, the current standard |
42
+ | `/sse` | Legacy SSE transport |
43
+ | `/authorize`, `/token`, `/register` | OAuth 2.1, S256 PKCE only |
44
+ | `/callback` | Where Wave returns after authorization |
45
+ | `/` | Landing page |
46
+ | `/privacy` | Privacy statement |
47
+ | `/delete` | Remove stored tokens for one user |
48
+
49
+ ## Operating it
50
+
51
+ Setup, secrets, deployment, and the post-deploy verification probes are in
52
+ [`worker/README.md`](../worker/README.md). The short version: `wrangler deploy`
53
+ reporting success proves nothing, so the deployment is not considered live
54
+ until the three unauthenticated probes pass.
@@ -0,0 +1,50 @@
1
+ # Privacy
2
+
3
+ ## The local server
4
+
5
+ `@oliverames/mcp-server-for-wave` runs on your own machine as a subprocess of
6
+ your MCP client. It has no backend and sends nothing anywhere except to Wave's
7
+ own API at `https://gql.waveapps.com/graphql/public`.
8
+
9
+ **Stored:** nothing. The server keeps no database, cache, or log file. Your
10
+ access token is read from the environment, a file you point it at, or 1Password,
11
+ and lives only in process memory.
12
+
13
+ **Transmitted:** your requests to Wave, and nothing else. There is no analytics,
14
+ telemetry, or error reporting.
15
+
16
+ **Logged:** diagnostics go to stderr, which your MCP client may capture. Access
17
+ tokens are redacted from error text before it is emitted.
18
+
19
+ Your accounting data flows from Wave, through this process, to your MCP client.
20
+ What that client then does with it, including whether it sends the content to a
21
+ model provider, is governed by that client's own privacy policy, not this one.
22
+
23
+ ## The hosted connector
24
+
25
+ The Cloudflare Worker in `worker/` is a different matter, because it stores
26
+ credentials on your behalf.
27
+
28
+ **Stored:** your Wave user ID, your Wave OAuth access and refresh tokens
29
+ (encrypted at the application layer before they reach storage, with a key
30
+ separate from the OAuth provider's), and whether you granted write access.
31
+
32
+ **Not stored:** your Wave password, which the connector never sees; and your
33
+ accounting data, which is fetched for a request and returned to your client
34
+ without being retained.
35
+
36
+ **Deleting your data:** use the connector's `/delete` page to remove the stored
37
+ tokens, then revoke the application in your Wave account settings.
38
+
39
+ ## Third parties
40
+
41
+ Wave Financial Inc. operates the API this software calls. Their handling of
42
+ your data is governed by [Wave's privacy policy](https://www.waveapps.com/legal/privacy-policy).
43
+
44
+ This project is not affiliated with Wave Financial Inc.
45
+
46
+ ## Questions
47
+
48
+ Open an issue at
49
+ <https://github.com/oliverames/wave-mcp-server/issues>, or email
50
+ oliverames@gmail.com.