Unofficial MCP server for the Akahu open-finance API (New Zealand). Personal Apps, read-by-default, write-with-consent.
Project description
nz-akahu-mcp
Unofficial MCP server for the Akahu open-finance API (New Zealand). Bring your own Akahu credentials; run locally over stdio; no hosted backend.
- 14 tools. Account & balance reads, settled & pending transaction reads, batch lookup by id, identity read, refresh writes, support-ticket writes, name verification.
- Read-only by default. Every write tool refuses until
AKAHU_READ_ONLY=false. - Per-call consent for every write. Each write calls
ctx.elicit()for confirmation in the client. - Personal Apps only. App-scoped endpoints, payments, and webhooks are not exposed.
Prerequisites
- Akahu Personal App + User token - register at https://my.akahu.nz/developers
uvfor theuvxrunner (or pip with a virtualenv)- Claude Code or Claude Desktop
Set your tokens once in your shell (or write them into the claude mcp add / config-file commands below):
export AKAHU_APP_TOKEN="app_token_..."
export AKAHU_USER_TOKEN="user_token_..."
# Optional - everything below is the default:
export AKAHU_READ_ONLY=true
export AKAHU_AUTOMATION_BYPASS=false
Install in Claude Code
Pick whichever fits your situation.
Option A: remote install via claude mcp add (recommended)
Once published to PyPI:
claude mcp add nz-akahu \
--scope user \
--env AKAHU_APP_TOKEN=app_token_... \
--env AKAHU_USER_TOKEN=user_token_... \
--env AKAHU_READ_ONLY=true \
-- uvx nz-akahu-mcp
Before a PyPI release, install straight from GitHub:
claude mcp add nz-akahu \
--scope user \
--env AKAHU_APP_TOKEN=app_token_... \
--env AKAHU_USER_TOKEN=user_token_... \
-- uvx --from git+https://github.com/severity1/nz-akahu-mcp nz-akahu-mcp
Scope options:
--scope user- available in every Claude Code project for your user (recommended for personal banking data)--scope project- shared via.mcp.jsonchecked into the repo (don't use this for tokens)--scope local- this project only, not shared (default)
Option B: local install via claude mcp add (clone + dev loop)
If you want to hack on the server:
git clone https://github.com/severity1/nz-akahu-mcp.git
cd nz-akahu-mcp
uv sync --extra dev
uv run pytest # 151 tests, 100% line + branch coverage
# Wire the local checkout into Claude Code:
claude mcp add nz-akahu \
--scope user \
--env AKAHU_APP_TOKEN=app_token_... \
--env AKAHU_USER_TOKEN=user_token_... \
-- uv --directory "$(pwd)" run nz-akahu-mcp
Restart your Claude Code session after editing source.
Option C: pip install from PyPI
Install into a virtualenv:
python -m venv ~/.venvs/nz-akahu-mcp
source ~/.venvs/nz-akahu-mcp/bin/activate # Windows: ~/.venvs/nz-akahu-mcp/Scripts/activate
pip install nz-akahu-mcp
Then point Claude Code at the installed console script (use the absolute path):
claude mcp add nz-akahu \
--scope user \
--env AKAHU_APP_TOKEN=app_token_... \
--env AKAHU_USER_TOKEN=user_token_... \
-- ~/.venvs/nz-akahu-mcp/bin/nz-akahu-mcp
To upgrade later: ~/.venvs/nz-akahu-mcp/bin/pip install --upgrade nz-akahu-mcp and restart Claude Code.
Verify the install
claude mcp list # confirms 'nz-akahu' is registered
claude mcp get nz-akahu # shows the resolved command and env
Inside Claude Code, ask: "What accounts do I have?" - it should call nz-akahu_accounts_list_accounts and return masked account details.
Install in Claude Desktop
Copy examples/claude_desktop_config.json into your Claude Desktop config and replace the placeholders. The config lives at:
- macOS:
~/Library/Application Support/Claude/claude_desktop_config.json - Windows:
%APPDATA%\Claude\claude_desktop_config.json - Linux:
~/.config/Claude/claude_desktop_config.json
Then restart Claude Desktop.
Run standalone (no client)
# Via uvx (zero-install):
uvx nz-akahu-mcp
# Via pip (after `pip install nz-akahu-mcp` in your venv):
nz-akahu-mcp
# From a local checkout:
uv run nz-akahu-mcp
The server speaks MCP over stdio. Inspect tool calls with:
uv run fastmcp dev src/nz_akahu_mcp/server.py
Architecture
+--- root FastMCP server (server.py) -----------+
| mounts 3 sub-servers, prints safety banner |
+----+---------------+--------------+-----------+
| | |
accounts transactions identity
| | |
+----- AkahuClient (httpx + retry) ----+
|
v
https://api.akahu.io/v1
(dual-header auth)
Every write tool is decorated with @require_write_consent from safety.py.
rg "@require_write_consent" enumerates writes; rg "automatable=True"
enumerates the bypass-eligible subset.
Read-only by default
The server starts with AKAHU_READ_ONLY=true. Every write tool refuses with
this message:
Write operations are disabled. To enable: set
AKAHU_READ_ONLY=falsein your.envand restart the MCP server. Each write will require your explicit confirmation through Claude (or setAKAHU_AUTOMATION_BYPASS=truefor the automatable subset only).
When writes ARE enabled, every write still calls ctx.elicit() to ask you in
Claude's UI before firing.
Automation bypass
Setting AKAHU_AUTOMATION_BYPASS=true together with AKAHU_READ_ONLY=false
skips the elicit prompt for tools marked automatable=True:
| Tool | Bypass-eligible? |
|---|---|
accounts/refresh_all_accounts |
YES |
accounts/refresh_account |
YES |
transactions/report_transaction_issue |
NO |
identity/verify_name |
NO |
Each bypassed call logs at INFO with the tool name. The server prints a startup banner listing the bypass-eligible tools.
Setting AKAHU_AUTOMATION_BYPASS=true with AKAHU_READ_ONLY=true is rejected
at startup.
Tool reference
Read tools (10)
accounts/
list_accounts- all connected accounts (masked, formatted balances)get_account(account_id)- single account detailsget_account_balance(account_id)- balance onlyget_pending_transactions(account_id)- not-yet-settled debits/credits for one account
transactions/
get_transactions(account_id?, start_date?, end_date?, category?, min_amount?, max_amount?, limit=100)get_transaction(transaction_id)get_transactions_by_ids(ids)- batch fetch by Akahu txn id (useful for webhook follow-up)get_pending_transactions- not-yet-settled across all accountssearch_transactions(query, limit=50)- substring across description + merchant.name
identity/
get_me
Write tools (4)
accounts/refresh_all_accounts(bypass-eligible)accounts/refresh_account(account_id)(bypass-eligible)transactions/report_transaction_issue(transaction_id, issue_type, fields?, comment?, other_transaction_id?)(always elicits)identity/verify_name(family_name, given_name?, middle_name?, initials?, account_id?)(always elicits) - requires Personal-App scope grant; without it Akahu returns 403
Privacy & security
- Account numbers are masked:
01-1234-1234567-00->01-****-***4567-00. - Tokens are never logged. The DEBUG-level body log is response-only.
- All filters are applied client-side after the API call returns. Akahu has no field-level access control for Personal Apps.
- The server runs entirely on your machine. The only outbound traffic is to
https://api.akahu.io/v1.
Personal Apps only
This server uses the Akahu Personal App model (per-user OAuth). The following endpoints are not exposed because they are app-scoped and unreachable from a Personal App:
/categories- NZFCC category taxonomy (transactions carry their category inline)/connections- supported-bank list/identity/{id}/verify-name- app-scoped variant; useidentity/verify_nameinstead
Full-app features are also out of scope:
- Payments (
make_payment,cancel_payment, etc.) - Webhooks (
subscribe_webhook, etc.)
Scope-grant gotchas
These user-scoped endpoints require a Personal-App scope toggled at https://my.akahu.nz/developers; without the grant they return 403:
GET /parties- counterparty list (not shipped as a tool)POST /verify/nameandPOST /verify/name/{id}- shipped asidentity/verify_name
Releasing
To cut a release: bump version in pyproject.toml, push a vX.Y.Z tag, then
publish a GitHub Release with that tag. The workflow runs the test+ruff+mypy
gate, asserts the pyproject version matches the tag, and publishes to PyPI via
Trusted Publishing (OIDC).
See PUBLISHING.md for the one-time PyPI setup and the
manual-publish fallback.
Disclaimer
This is an unofficial integration. Not affiliated with Akahu, your bank, or any financial institution. Use at your own risk. The maintainers are not responsible for any data loss, unauthorised access, or financial impact arising from use of this software.
License
Apache-2.0. See LICENSE.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file nz_akahu_mcp-0.1.0.tar.gz.
File metadata
- Download URL: nz_akahu_mcp-0.1.0.tar.gz
- Upload date:
- Size: 122.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d09273a115c531f5ee74afaee6f78d49d532a1e7fd015ccb2c4eac7cb6d6dc9b
|
|
| MD5 |
e3b3f600ea72526f501b34ac568ee82a
|
|
| BLAKE2b-256 |
7358c535cfd939e6022dbddcd643995ee0360ce3824528685cd6ddae01c8c044
|
Provenance
The following attestation bundles were made for nz_akahu_mcp-0.1.0.tar.gz:
Publisher:
release.yml on severity1/nz-akahu-mcp
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
nz_akahu_mcp-0.1.0.tar.gz -
Subject digest:
d09273a115c531f5ee74afaee6f78d49d532a1e7fd015ccb2c4eac7cb6d6dc9b - Sigstore transparency entry: 1622951227
- Sigstore integration time:
-
Permalink:
severity1/nz-akahu-mcp@8e549ac093949d037af2794bdb9f12cf9e92467a -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/severity1
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@8e549ac093949d037af2794bdb9f12cf9e92467a -
Trigger Event:
release
-
Statement type:
File details
Details for the file nz_akahu_mcp-0.1.0-py3-none-any.whl.
File metadata
- Download URL: nz_akahu_mcp-0.1.0-py3-none-any.whl
- Upload date:
- Size: 25.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4cfb1a392cdf21fe6c16cbd0083b9853a47fee29e9664e77f64acb25911c80b6
|
|
| MD5 |
0ca5b557aa0cf9938252eaefe128c557
|
|
| BLAKE2b-256 |
8ca86498a848823702e1f7191b4b0897d8ccf537d24605a4cc050a031ad7bcf3
|
Provenance
The following attestation bundles were made for nz_akahu_mcp-0.1.0-py3-none-any.whl:
Publisher:
release.yml on severity1/nz-akahu-mcp
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
nz_akahu_mcp-0.1.0-py3-none-any.whl -
Subject digest:
4cfb1a392cdf21fe6c16cbd0083b9853a47fee29e9664e77f64acb25911c80b6 - Sigstore transparency entry: 1622951316
- Sigstore integration time:
-
Permalink:
severity1/nz-akahu-mcp@8e549ac093949d037af2794bdb9f12cf9e92467a -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/severity1
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@8e549ac093949d037af2794bdb9f12cf9e92467a -
Trigger Event:
release
-
Statement type: