MCP Server for Wave

MCP Server for Wave

Provides complete MCP access to Wave Accounting, enabling AI assistants to manage invoices, payments, customers, and bookkeeping via natural language.

Category
访问服务器

README

<p align="center"> <img src="assets/icon.png" width="80" height="80" alt="Wave MCP Server"> </p>

<h1 align="center">MCP Server for Wave</h1>

<p align="center"> <strong>Complete access to Wave Accounting from Claude, Codex, and any other MCP client</strong> </p>

<p align="center"> <code>74 tools</code> • <code>42/42 mutations</code> • <code>11/11 queries</code> • <code>7 resources</code> </p>

<p align="center"> <a href="https://www.npmjs.com/package/@oliverames/mcp-server-for-wave"> <img src="https://img.shields.io/npm/v/@oliverames/mcp-server-for-wave?style=flat-square&color=f5a542" alt="npm"> </a> <a href="LICENSE"> <img src="https://img.shields.io/badge/license-MIT-f5a542?style=flat-square" alt="License"> </a> <a href="https://www.buymeacoffee.com/oliverames"> <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"> </a> </p>


Wave gives small businesses free accounting and invoicing, and a GraphQL API that covers nearly all of it. This server puts that entire API in front of an AI assistant: invoices and payments, estimates and deposits, customers, vendors, products, sales taxes, the chart of accounts, and double-entry bookkeeping.

Every query is verified against Wave's live schema in CI, and the tools that change or send anything stay hidden until you turn them on.

Why This Exists

Bookkeeping is mostly translation. You have a receipt, a bank line, an email promising to pay next week, and none of it is in the shape your books want. The work is not hard, it is just constant, and it is exactly the kind of task worth handing to an assistant that can hold the whole picture at once.

Doing that well needs more than a few convenience endpoints. An assistant that can list invoices but not record the payment, or draft an estimate but not convert it, forces you back into the web app halfway through every task. So this server covers the API completely: all 42 mutations, all 11 root queries, every sub-resource on a business. If Wave's API can do it, a tool here does it.

Two decisions shape the rest:

Writes are off by default. Wave has genuinely irreversible operations. Sending an invoice emails a real customer. Deleting one is permanent. A default install exposes 30 read-only tools; the other 44 appear only when you set WAVE_ALLOW_WRITES=1. Reading your books should not require trusting a model with your outbox.

Errors explain themselves. Wave rejects an unbalanced transaction without telling you which figure is wrong. This server compares the anchor against the line items first and reports the difference. A category word that matches no account produces the list of real account names rather than a silent guess at the first one.

Quick Start

{
  "mcpServers": {
    "wave-mcp-server": {
      "command": "npx",
      "args": ["-y", "@oliverames/mcp-server-for-wave@latest"],
      "env": {
        "WAVE_ACCESS_TOKEN": "your_token_here"
      }
    }
  }
}

Then ask for your businesses and set one as the default:

List my Wave businesses and set the first one as the default.

Get a token

Create an application and generate an access token in the Wave developer portal. Wave's tokens expire, so expect to refresh it periodically, or use the hosted connector which handles refresh for you.

Install as a plugin

The repo doubles as a single-plugin marketplace for hosts that support them:

/plugin marketplace add oliverames/wave-mcp-server
/plugin install wave-mcp-server

Install in Codex

codex mcp add wave-mcp-server \
  --env WAVE_ACCESS_TOKEN=your_token_here \
  -- npx -y @oliverames/mcp-server-for-wave@latest

Verify with codex mcp list. Startup takes about 0.2s, well inside Codex's 10-second startup_timeout_sec, and the retry budget is capped below its 60-second tool_timeout_sec so a slow API surfaces Wave's real error rather than a client timeout.

Enable write tools

"env": {
  "WAVE_ACCESS_TOKEN": "your_token_here",
  "WAVE_ALLOW_WRITES": "1"
}

This registers the 44 tools that create, change, delete, or email records. Without it they are not advertised at all, so a model cannot call one by guessing its name.

Docker

docker build -t wave-mcp-server .
docker run --rm -i -e WAVE_ACCESS_TOKEN=your_token_here wave-mcp-server

The -i matters: the server speaks JSON-RPC on stdin and stdout.

1Password token lookup

Rather than pasting a token into a config file, point the server at a secret reference and it will shell out to the op CLI on startup:

"env": { "WAVE_OP_PATH": "op://Development/Wave/credential" }

WAVE_ACCESS_TOKEN_FILE works the same way for a file on disk.

What You Can Do

Bill a customer end to end

Create an invoice for Acme Corp with 10 hours of consulting at $150/hour,
due in 30 days. Approve it and email it to billing@acme.com.

Quote, then convert

Create an estimate for the website redesign package with a 25% deposit,
send it, and convert it to an invoice once they accept.

Record a receipt

Log a $45.99 expense from Office Depot on 2026-03-15 for office supplies,
paid from Business Checking.

Split a transaction

Record a $100 withdrawal from checking: $60 to fuel and $40 to meals.

Reconcile a processor payout

A Stripe payout of $97 landed in checking: $100 of consulting income
less a $3 processing fee.

Chase what is owed

Show me every unpaid invoice over $500, sorted by amount due, and which
customers carry the largest overdue balances.

Tools Reference

Names are prefixed wave_ so they do not collide with other MCP servers. Tools marked W require WAVE_ALLOW_WRITES=1.

Businesses and reference data

Tool Purpose
wave_list_businesses List reachable businesses
wave_get_business Full business detail
wave_set_default_business Set the session default
wave_get_invoice_estimate_settings Accent color and logo
wave_auth_status How credentials resolved, and what is gated. Makes no API call
wave_get_user Account the token belongs to
wave_get_oauth_application Application that issued the token
wave_list_currencies / wave_get_currency Supported currencies
wave_list_countries / wave_get_country Countries and their provinces
wave_get_province One province or state
wave_list_account_types The five top-level account types
wave_list_account_subtypes Subtypes, which wave_create_account requires

Chart of accounts

Tool Purpose
wave_list_accounts / wave_get_account Accounts with balances
wave_create_account W Add an account
wave_patch_account W Rename or renumber
wave_archive_account W Hide from pickers, keep history

Customers, vendors, products, taxes

Tool Purpose
wave_list_customers / wave_get_customer Customers with balances
wave_create_customer / wave_patch_customer / wave_delete_customer W Manage customers
wave_list_vendors / wave_get_vendor Vendors (read-only in Wave's API)
wave_list_products / wave_get_product Products and services
wave_create_product / wave_patch_product / wave_archive_product W Manage products
wave_list_sales_taxes / wave_get_sales_tax Taxes and rate history
wave_create_sales_tax / wave_patch_sales_tax / wave_archive_sales_tax W Manage taxes

Invoices and payments

Tool Purpose
wave_list_invoices / wave_get_invoice Invoices with items and payments
wave_create_invoice / wave_patch_invoice / wave_clone_invoice W Build invoices
wave_approve_invoice W Move a draft into the books
wave_send_invoice W Emails the customer
wave_mark_invoice_sent W Record delivery made outside Wave
wave_delete_invoice W Permanent
wave_get_invoice_payment One payment
wave_create_invoice_payment / wave_patch_invoice_payment / wave_delete_invoice_payment W Record payments
wave_send_invoice_payment_receipt W Emails the customer

Estimates and deposits

Tool Purpose
wave_list_estimates / wave_get_estimate Estimates with history and deposits
wave_create_estimate / wave_patch_estimate / wave_clone_estimate W Build estimates
wave_approve_estimate W Approve a draft
wave_send_estimate W Emails the customer
wave_mark_estimate_sent / wave_mark_estimate_accepted W Record offline delivery and acceptance
wave_reset_estimate_acceptance W Undo an acceptance
wave_send_estimate_acceptance_email W Emails the customer
wave_generate_estimate_pdf W Render a PDF
wave_convert_estimate_to_invoice W Turn an accepted estimate into an invoice
wave_delete_estimate W Permanent
wave_get_estimate_payment One deposit payment
wave_create_estimate_deposit_payment / wave_update_estimate_deposit_payment / wave_delete_estimate_payment W Record deposits
wave_send_estimate_deposit_receipt W Emails the customer

Bookkeeping

Tool Purpose
wave_create_money_transaction W One expense, income, or transfer
wave_create_money_transactions W Bulk import, applied atomically
wave_create_deposit_transaction W A payout whose net differs from gross
wave_create_expense_from_receipt W Expense, account matched from a category word
wave_create_income_from_payment W Income, account matched from a category word

Resources

Read-only JSON views for grounding context. Everything here is also reachable through a tool, so hosts that ignore resources lose nothing.

wave://businesses • wave://accounts • wave://customers • wave://vendors • wave://products • wave://sales-taxes • wave://account-taxonomy

How Transactions Work

Wave is double-entry, so wave_create_money_transaction has two sides:

  • The anchor is the account money physically moved through, a bank account or credit card, with a direction of DEPOSIT or WITHDRAWAL.
  • The line items are the categories it is attributed to. Their amounts must total the anchor amount.

A $50 office-supplies expense paid from checking is one anchor (checking, WITHDRAWAL, 50.00) and one line item (Office Supplies, 50.00). A split is the same anchor with more line items.

Every transaction carries an external_id. Wave deduplicates on it, so passing a stable value of your own makes retries safe; one is generated when you omit it.

Environment Variables

Variable Required Default Description
WAVE_ACCESS_TOKEN Yes (none) OAuth2 bearer token from the Wave developer portal
WAVE_BUSINESS_ID No (none) Default business, so tools can omit business_id
WAVE_ALLOW_WRITES No off Set to 1 to register the 44 tools that change or send data
WAVE_ACCESS_TOKEN_FILE No (none) Read the token from a file instead
WAVE_OP_PATH No (none) Read the token from 1Password, e.g. op://Vault/Item/credential
WAVE_TIMEOUT_MS No 20000 Per-request timeout
WAVE_TOTAL_BUDGET_MS No 50000 Total time for one call including retries
WAVE_MAX_RESPONSE_BYTES No 8388608 Reject responses above this size
WAVE_HTTP_RETRIES No 2 Retries on 429 and 5xx
WAVE_DISABLE_AGENT_CONFIG_FALLBACK No off Read only the environment, not agent config files

Credentials resolve in order: environment, then the host agent's own config file, then WAVE_ACCESS_TOKEN_FILE, then 1Password. Reading the agent config matters because MCP clients launch this server as a subprocess, so a value in claude_desktop_config.json or ~/.codex/config.toml reaches it only if the user wired it through by hand.

Amount Handling

Money is sent to Wave as strings, not floats, so 0.1 + 0.2 cannot become 0.30000000000000004 on the way to your ledger. Balance checks compare minor units as integers for the same reason.

One exception, and it is Wave's: moneyDepositTransactionCreate types its amounts as Float rather than Decimal, so wave_create_deposit_transaction sends numbers there because the API accepts nothing else.

Wave API Limitations

These are constraints in Wave's API, not gaps here. Each was confirmed against the live schema.

  • Transactions cannot be read back. Wave creates money transactions but exposes no query to list them; there is no transactions connection on Business. Review them in the web app.
  • Vendors are read-only. The schema has no vendorCreate, vendorPatch, or vendorDelete.
  • Money transactions cannot reference a vendor. wave_create_expense_from_receipt records the name in the description.
  • wave_patch_estimate demands fields you are not changing. Wave marks seven of them required on the patch input; read the estimate first and pass its current values back.
  • wave_patch_account needs the account's current sequence as an optimistic-concurrency check.
  • Line items must reference a product. No free-text lines.
  • wave_create_deposit_transaction returns no ID.
  • Bills, receipts, payroll, and reports have no API.
  • No file attachments. Receipt images cannot be uploaded.
  • Rate limits are tight, roughly two concurrent requests.
  • Un-archiving is web-app only.

Hosted Connector

A Cloudflare Worker serves the same tools over OAuth instead of a shared token. Users authorize against their own Wave account, tokens are encrypted before storage, and write access is chosen at authorization time so a read-only connection cannot be escalated later.

The deployment at https://wave.amesvt.com/mcp is private: an owner allowlist restricts it to one Wave account, and any other account is refused before a token is stored. Deploy your own copy from worker/ to use it.

See worker/README.md for setup and the security model.

Architecture

index.js                     Single-file server: client, 74 tools, 7 resources
  createWaveServer()         Factory over injected credentials, shared by
                             the stdio process and the hosted Worker
scripts/
  smoke-validate-graphql.mjs Schema-check every query against live Wave
  smoke-list-tools.mjs       Start over stdio and enumerate what is advertised
  smoke-packed-install.mjs   Pack, install, and launch the way npx does
  sync-plugin-metadata.mjs   Propagate the version to every host manifest
  check-release-consistency  Fail the build when anything disagrees
  build-mcpb.mjs             Desktop bundle
worker/                      Hosted OAuth connector
test/unit.test.mjs           58 tests, no network

The tool layer lives in one file on purpose. It is imported unchanged by the Worker, so the hosted and local servers cannot drift apart.

Verification without credentials

Wave validates a GraphQL document and coerces its variables before it checks authentication. An UNAUTHENTICATED response therefore means the query is correct, while GRAPHQL_VALIDATION_FAILED means it is not.

CI exploits that to schema-check all 64 documents on every push with no token at all, which catches a field Wave renames before a user does.

Building

npm install
npm test                  # 58 unit tests, no network
npm run smoke:list-tools  # start over stdio, enumerate tools
npm run smoke:packed      # pack, install, and launch via the bin symlink
npm run smoke:schema      # validate every query against live Wave
npm run release:check     # version parity across 8 manifests
npm run build:mcpb        # desktop bundle

Contributions welcome. See CONTRIBUTING.md.

Not Affiliated With Wave

An independent project, not affiliated with, endorsed by, or sponsored by Wave Financial Inc. Wave Financial Inc. owns the Wave name, logo, and marks; the icon above is theirs and is used only to identify the service this server connects to. Originally forked from vinnividivicci/wave_mcp, then rewritten.


<p align="center"> <a href="https://www.buymeacoffee.com/oliverames"> <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"> </a> </p>

<p align="center"> <sub> Built by <a href="https://ames.consulting">Oliver Ames</a> in Vermont • <a href="https://github.com/oliverames">GitHub</a> • <a href="https://linkedin.com/in/oliverames">LinkedIn</a> • <a href="https://bsky.app/profile/oliverames.bsky.social">Bluesky</a> </sub> </p>

推荐服务器

Baidu Map

Baidu Map

百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。

官方
精选
JavaScript
Playwright MCP Server

Playwright MCP Server

一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。

官方
精选
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。

官方
精选
本地
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。

官方
精选
本地
TypeScript
VeyraX

VeyraX

一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。

官方
精选
本地
Kagi MCP Server

Kagi MCP Server

一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。

官方
精选
Python
graphlit-mcp-server

graphlit-mcp-server

模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。

官方
精选
TypeScript
mcp-server-qdrant

mcp-server-qdrant

这个仓库展示了如何为向量搜索引擎 Qdrant 创建一个 MCP (Managed Control Plane) 服务器的示例。

官方
精选
e2b-mcp-server

e2b-mcp-server

使用 MCP 通过 e2b 运行代码。

官方
精选
Neon MCP Server

Neon MCP Server

用于与 Neon 管理 API 和数据库交互的 MCP 服务器

官方
精选