Enable Banking

com.foreverworks由 foreverworks.com 發布v0.1.1更新於 Nov 3, 2026

Read, search and export statements and balances from banks across Europe, without moving money.

已驗證STDIO僅桌面金融資料與分析

概覽

AI 產生的概覽

透過 Enable Banking 以 PSD2 唯讀方式存取銀行帳戶:查看授權、帳戶、餘額與交易,並可匯出 CSV 對帳單。

可以做什麼
  • 檢查整合健康狀態、所用應用程式、環境與已存授權數量
  • 依國家列出可連接的銀行,列出已存授權及其到期時間,以及授權可存取的帳戶
  • 讀取單一帳戶、單一授權或全部授權下的餘額與交易,可依文字、收支方向、金額與日期篩選
  • 將同一份對帳單匯出為 CSV 檔案,並回傳路徑、大小與雜湊值
  • 發起、完成與撤銷銀行授權(每次授權需帳戶持有人在其銀行 App 中確認)
適用情境
適合讓助理回答歐洲銀行對帳單與餘額問題、依交易對象或金額查找付款,或為會計匯出 CSV;不涉及發起付款。
執行需求
透過 npx 在本機執行,需要 Node.js 22.19 或更新版本。需要一個 Enable Banking 應用程式(應用程式 ID、已註冊的重新導向網址、RS256 私密金鑰 PEM),並以 ENABLE_BANKING_CONFIG 指定 app.json 路徑;ENABLE_BANKING_KEY 可選填以指向金鑰。每次授權都需帳戶持有人在其銀行 App 中核准,且需能連線至 Enable Banking API 的網路。
安裝前請注意
私密金鑰檔案與 app.json 屬敏感憑證,應僅允許此伺服器讀取。授權會給予 180 天、涵蓋銀行提供之全部帳戶的讀取權限,start_auth 應由本人確認。complete_auth 與 revoke_session 會變更已存授權,撤銷後須由持有人重新核准才能再讀取。對帳單資料會傳送至 Enable Banking 的 API,並寫入本機 CSV 檔案。銀行對每個帳戶每天僅允許約四次讀取。

安裝

在 SourceWeft 中

  1. 開啟 儀表板中的 Enable Banking,將其新增到工作區。
  2. 為需要使用其工具的對話啟用該服務。

Desktop only,透過 STDIO。 STDIO 服務會啟動本機處理程序,因此需要 SourceWeft 桌面主機。

其他 MCP 客戶端

參照 儲存庫 中的啟動說明。

README

[Enable Banking icon] Enable Banking MCP server

Read, search and export your bank statements and balances from your AI assistant, through Enable Banking, a licensed PSD2 provider for banks across Europe. It moves no money: besides reading, it only starts, stores and ends consents. start_auth is its one money tool, because it asks your bank for 180 days of read access; your client asks you before each call.

You need an Enable Banking application (its id, a registered redirect URL and its private key), and the account holder, to approve each consent in their bank's app every 180 days. A bank allows about four reads of an account a day without the holder present, shared by every program on that consent, so read a period once and narrow it here.

What you can ask

  • "Which bank consents do we have, and when does each one run out?"
  • "What did we pay Example Supplier SRL this year?"
  • "Show incoming payments over 1,000 RON in September, across all our accounts."
  • "What is the available balance on our EUR account?"
  • "Export last quarter's transactions as CSV for our accountant."
  • "Our consent with Example Bank ends next week. Renew it."

Install

[Install in VS Code] [Install in Cursor] [Download for Claude Desktop]

With Claude Code:

bash
claude mcp add enable-banking -e ENABLE_BANKING_CONFIG="<path to Enable Banking app.json>" -- npx -y @foreverworks/mcp-enable-banking

Claude Desktop (Settings, Developer, Edit Config) and most other clients read the same command as JSON:

json
{  "mcpServers": {    "enable-banking": {      "command": "npx",      "args": ["-y", "@foreverworks/mcp-enable-banking"],      "env": {        "ENABLE_BANKING_CONFIG": "<path to Enable Banking app.json>"      }    }  }}

Each runs the published package on Node 22.19 or newer. VS Code and the Claude Desktop bundle ask for each setting as they add the server; leave an optional one empty. Cursor reads each from your environment instead (${env:ENABLE_BANKING_CONFIG} and so on), so set them before you start Cursor. The commands above set only what is required; Configuration has the rest.

From a clone

bash
git clone https://github.com/foreverworks/mcps.git && cd mcpsnpm ci && npm run buildclaude mcp add enable-banking -e ENABLE_BANKING_CONFIG="<path to Enable Banking app.json>" -- node "$PWD/servers/enable-banking/dist/index.js"

In a JSON configuration, the command is node and its one argument the full path of servers/enable-banking/dist/index.js. To share one running copy between several clients over HTTP, see docs/deploying.md.

Configuration

VariableRequiredMeaning
ENABLE_BANKING_CONFIGyesPath of app.json (below), which the server only reads.
ENABLE_BANKING_KEYnoPath of the application's private key (PEM). When unset, app.json's private_key names it, relative to app.json's folder.
ENABLE_BANKING_STATE_DIRnoWhere the consents (sessions.json), their backup and their lock live; the default is in docs/configuration.md.
ENABLE_BANKING_EXPORTS_DIRnoWhere export_transactions writes its CSV files. Default: exports/ in the state folder.

app.json holds the application's details from the control panel:

json
{  "application_id": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",  "application_name": "Example Books",  "environment": "sandbox",  "redirect_url": "https://example.com/bank-callback",  "private_key": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee.pem"}

application_id and redirect_url are required, and private_key too unless ENABLE_BANKING_KEY is set. redirect_url must match a registered one exactly. application_name and environment only label health's answer; the application_id decides sandbox or production. api_base overrides https://api.enablebanking.com for tests or a proxy; other keys are ignored. Under systemd, a credentials directory renames files to their credential names, so set ENABLE_BANKING_KEY there. The variables every server shares are in docs/configuration.md.

Getting the application and its key

  1. In Enable Banking's Control Panel, open API applications from the top menu. In "Add a new application", keep the Sandbox environment: its test banks let you try every tool without a real account.
  2. Name the application (the account holder sees the name) and enter a redirect URL. Any page you control works: complete_auth only needs the address the browser lands on.
  3. Keep the default private key option and press Register. Your browser generates the key and saves it as <application id>.pem; move it where only this server can read it, and write app.json beside it.

Production is a second application, registered with the Production environment. It starts Inactive; "Activate by linking accounts" makes it active in restricted mode, which reads only your own linked accounts until you sign a contract with Enable Banking.

First run

  1. Ask your assistant to run Enable Banking's health check. It names the application, its environment, whether it is active and its redirect URLs, and counts the stored consents. A key that does not belong to the application_id fails here.
  2. Find your bank with list_aspsps for its country. start_auth takes its name; psuTypes lists its account types (business, personal), and maxConsentDays its longest consent.
  3. Ask for start_auth with that name, the country and the psu_type, and approve it. The link it returns is for the account holder: plan five minutes with the phone that holds the bank's app.
  4. Open the link, sign in to the bank, and tick every account it offers. The request names no accounts, which asks for all of them.
  5. The bank sends you back to the redirect URL with ?code=.... Give that address (or the code) to complete_auth at once: the code works once, for minutes. Its answer lists the accounts and the consent's end date.

To renew a consent (list_stored_sessions says how many days it has left), repeat steps 3 to 5 with its bank, country and type. Without a label, the new consent takes the old one's place and label.

From a shell, node dist/index.js --health prints one ENABLEBANKING: line and exits 1 when a person has to act or 2 when Enable Banking was unwell, which suits a daily timer.

Tools

read changes nothing at the bank or in the stored consents (an export writes only its file); write changes the stored consents; money asks a bank for access to your accounts.

ToolAccessWhat it does
healthreadWhether Enable Banking accepts the key, as which application and in which environment, where the files are, and how many consents are stored.
list_aspspsreadThe banks Enable Banking reaches in a country (empty for every market): the name start_auth takes, the account types each offers and its longest consent. Cached an hour.
list_stored_sessionsreadEvery stored consent by label, with its bank, its accounts and when it expires, as a date and as days left.
start_authmoneyStarts a consent with a bank, for every account it offers, for 180 days. Returns a link the account holder must open.
complete_authwriteFinishes a consent from the bank's redirect, stores it under a label, and reports its accounts and its end date.
revoke_sessionwriteEnds a stored consent at the bank and removes it here, keeping the previous file as a backup.
list_accountsreadThe accounts a consent reaches today, asked of the bank, each with the uid the other tools take.
get_transactionsreadA statement for one account, every account on a consent, or every stored account, with exact signed amounts, narrowed by text, direction and amount without another read at the bank.
export_transactionsreadThe same statement written as a CSV file, for a long period, many accounts or an accountant. Returns the file's path, size and SHA-256.
get_balancesreadEvery balance the bank reports for one account (booked, available and the others it sends), each an exact amount with its PSD2 code and its meaning.

Money

start_auth moves no money, but it grants 180 days of read access, which only the account holder should decide. It is marked money in tools/list, so your client asks you before each call (Claude Code does, in every permission mode). With MCP_CONFIRM=elicit the server asks you itself, in one line computed from the arguments:

enable-banking: Bank consent (PSD2, read-only: balances and transactions) with Example Bank (DE), personal accounts, every account the bank offers, valid 180 days: until 2030-07-01 if confirmed today

The end date is computed when you are asked and again when the call runs, so a later confirmation moves it by the delay. Access starts when the account holder approves in their bank.

Troubleshooting

config: "ENABLE_BANKING_CONFIG is not set", or a message naming app.json. Set ENABLE_BANKING_CONFIG to a readable app.json, and ENABLE_BANKING_KEY (or private_key) to the key.

auth: "cannot use the private key at ...". The key file is missing, unreadable or not an RS256 private key. Point ENABLE_BANKING_KEY or private_key at the .pem the control panel saved; the next call reads it again, with no restart.

api with status 401 or 403. Enable Banking refused the signed token: the key does not belong to the application_id (a sandbox key beside a production id, say).

api with reason EXPIRED_SESSION, REVOKED_SESSION, CLOSED_SESSION or SESSION_DOES_NOT_EXIST, or status 422 or 404, on a call that worked the day before. The consent lapsed or was revoked in the bank's app, and retrying will not bring it back. Renew it with steps 3 to 5 of First run.

api from complete_auth with reason EXPIRED_AUTHORIZATION_CODE, WRONG_AUTHORIZATION_CODE or outcome_unknown. The code works once, for minutes, and a dropped exchange cannot be repeated. Run start_auth again and call complete_auth right after approving.

api with reason: "ASPSP_RATE_LIMIT_EXCEEDED". The account's reads are spent for the day. Wait a day, then read each period once and narrow it with the filters.

input: "the bank refused the consent: ...". The account holder declined, or the bank failed; start again with start_auth. "no code parameter there" means you pasted start_auth's link, not the address the bank sent you back to.

How it works

Statements

Amounts stay decimal strings, never JavaScript numbers, which keep only 15 to 17 significant digits (9007199254740993.01 becomes 9007199254740992). Enable Banking sends an unsigned amount and a credit_debit_indicator; get_transactions signs the text, negative for money out. A row whose amount came as a JSON number, or not as a plain decimal, carries an amountWarning.

Search runs here, after the read: a bank only returns every row in a date range. A filtered answer echoes the filter, with fetched (rows read) beside count (rows matched).

  • text looks in the counterparty's name and IBAN, the payment reference and the entry reference, ignoring case, diacritics and an IBAN's spaces: santier finds ȘANTIER EXEMPLU SRL and the cedilla Şantier some banks still send.
  • direction is credit (money in) or debit (money out).
  • amount_min and amount_max bound the size, sign ignored, inclusive, as exact decimals; a row whose amount could not be read exactly is left out and counted in amountUnreadable.
  • date_from and date_to are inclusive; empty is unbounded. transaction_status defaults to booked and takes a name (pending, cancelled, ...) or a PSD2 code; empty leaves the bank's default.

Several accounts are read one at a time, since banks throttle bursts, and each row names its account. A refused account appears in accounts with its error and remedy, beside the others' rows. Arguments are checked before the first read, an account uid (a UUID that changes with every consent) for its form, so a typo spends nothing. Pages are followed to the end, up to 200.

The counterparty is the other side. On a debit, debtor_account is your own IBAN, so the side is picked by direction. Remittance lines are joined with one space, because banks split references anywhere.

Exports have the columns account_label, account_iban, booking_date, value_date, direction, amount, currency, counterparty_name, counterparty_iban, remittance, reference, status, bank_code, merchant_category_code, amount_warning. Every cell is quoted; one a spreadsheet would run as a formula (starting with =, + or @) gets a leading apostrophe, but a negative amount does not. Each file is named for its scope, dates and creation time, so it never replaces one someone has open, and is written at mode 0640.

Consents and sessions.json

A consent is stored before anything else, because Enable Banking reveals a session id exactly once, and another costs the account holder a new authentication. complete_auth exchanges the code, stores the result, then asks for each account's details; if that fails, the consent stands and the account carries a detailsError. A new bank's consent is labelled <bank>_<country> (example_bank_de) unless you give a label.

sessions.json is a format other programs can read, defined in src/domain/sessionsFile.ts:

  • an object keyed by label (a-z 0-9 _ -, an identifier anywhere);
  • each entry the POST /sessions response verbatim: session_id, accounts (with uid, account_id.iban, identification_hash, ...), aspsp, psu_type, access.valid_until;
  • two-space JSON at mode 0600, written atomically under a lock, other labels carried over. The previous file is re-serialised into sessions.json.bak first, or kept aside as sessions.json.unreadable-<ms> if it is not a JSON object.

identification_hash identifies an account across consents, linking a renewal to the history of the one it replaced. When an exchange lacks such a field, complete_auth stores the consent anyway and reports formatWarnings.

Ending a consent closes the session at Enable Banking, which ends the bank's consent, then removes the label; one the bank already ended is removed too. If the bank refuses, nothing changes here.

Requests

There is no grant to refresh. Each attempt carries a JWT signed on the spot with the private key (RS256, valid five minutes, the application_id as kid), so a failing health means the key or the id, never an expiry. src/infra/http.ts tries a read up to four times on a 5xx or a dropped connection, takes a 401 or a 422 as final, and never resends a POST Enable Banking may have acted on.

Wire types are generated by openapi-typescript from Enable Banking's OpenAPI description (npm run gen:types -w @foreverworks/mcp-enable-banking; read the diff). src/domain/schema.ts adds two measured adjustments: an optional field may be null (a statement's last page has continuation_key: null), and an amount is string | number.

Only the bank catalogue is cached (an hour per country, in memory): it is the same for everyone. npx tsx scripts/smoke.ts reads one statement over the last three days from a real consent, and with --consent also renews the consent, which needs the account holder.

Limitations

  • Account information only (AIS): no payment initiation, none planned.
  • A consent needs the account holder, to make it and to renew it: the bank's authentication is the point of PSD2.
  • Every consent asks for 180 days and every account. A bank with a shorter maxConsentDays refuses it rather than shortening it.
  • No history of its own (no cache, alerts or categories), so every statement is a read at the bank. A program that needs fewer reads should keep its own ledger, which sessions.json is designed to feed.
  • One application per server: sandbox and production need a configuration each.

Other servers for Enable Banking

Searched 2026-10-21. Enable Banking publishes none: its GitHub organisation has API samples, a command line tool and an eIDAS broker, and its site does not mention MCP. Four community servers read accounts through it:

  • enable-banking-mcp on npm, the most downloaded (752 in the month to 2026-10-18): stdio on macOS, secrets in the Keychain, for personal, noncommercial use only.
  • bankmcp on npm (555 that month): local, or hosted behind its own sign-in, with watches that call a webhook. Amounts become floating-point numbers.
  • banking-mcp on GitHub: a Cloudflare Worker behind OAuth that keeps balances and transactions in its own database, with categories and exports; banks are linked from a command line, not a tool.
  • bank-mcp by elcukro on GitHub: Enable Banking beside Plaid, Tink and Teller. Amounts become floating-point numbers.

This one exists for what none of them does: amounts stay exact decimal strings from the bank to the answer; starting a consent is a money tool whose confirmation names the bank, the accounts and the end date; and consents are kept by label in a file other programs can read, with a backup of the previous one and, per account, the hash that ties a renewal to its history. It lacks what the others add: alerts (bankmcp), a local cache and categories (banking-mcp), and banks beyond Enable Banking (bank-mcp).

來源:servers/enable-banking/README.md,提交 cbd1121

工具

10

complete_auth

Finish a bank consent

Exchange the code from the bank's redirect for a session and store it under label, replacing the consent stored there (the previous sessions.json is copied to sessions.json.bak first); without a label, a renewal replaces the stored consent for the same bank, country and psu_type. Takes the WHOLE redirect URL or just its code. The code is single-use and expires within minutes, so run this straight after the approval. The session is written to disk before anything else, because Enable Banking reveals the session id exactly once. Returns the authorised accounts (IBAN, currency, name, as the bank describes them now) and when the consent ends.

輸入參數labelcode_or_url必填

export_transactions

Export transactions as CSV

The same statement as get_transactions (same arguments, same filters, same reads at the bank), written as a CSV file instead of returned: one row per transaction with its account, dates, direction, signed exact amount, currency, counterparty, payment reference and status. Returns the file's path, size and SHA-256, and how many rows it holds. Use it for a long period or many accounts, or when the result is meant for a spreadsheet or an accountant.

輸入參數textlabeldate_todate_fromdirectionamount_maxamount_minaccount_uidtransaction_status

get_balances

Get balances

The balances the bank reports for one account uid: every type it sends (booked, available, interim, and so on), each with the PSD2 code as sent and its meaning spelled out, a reference date where the bank gives one, and the amount as an EXACT decimal string signed by the bank (negative when overdrawn). One read at the bank per call, like a statement.

輸入參數account_uid必填

get_transactions

Get transactions

A statement: the transactions of one account (account_uid), of every account on one consent (label), or of every account on every stored consent (neither), following the bank's pagination to the end. Each account read spends one of the few reads the bank allows per account per day (about four), so read once and narrow with the filters rather than reading again: text, direction, amount_min and amount_max run here, after the read, and cost nothing more. Dates are YYYY-MM-DD and empty means unbounded. transaction_status takes a friendly name (booked, pending, cancelled, hold, other, rejected, scheduled) or a PSD2 code; empty omits the filter, which is NOT the same as asking for booked. Each row carries a SIGNED decimal amount as a string, negative for money leaving the account, and a counterparty resolved against the direction rather than copied from one fixed field. Amounts are strings because they are exact: parse them yourself if you need arithmetic. An amountWarning appears only on a row whose figure could not be taken at face value. Reading several accounts, each row also names its account, and one account that fails is reported in accounts without hiding the others. For more rows than a reply should hold, export_transactions writes the same statement as CSV.

輸入參數textlabeldate_todate_fromdirectionamount_maxamount_minaccount_uidtransaction_status

health

Health

Check that the integration is alive and say so in detail: the Enable Banking application the key pair authenticates as, its environment and redirect URL, where the config, key and sessions are, and how many consents are stored. Run this before concluding the MCP is broken.

無需輸入參數。

list_accounts

List accounts on a consent

The accounts a stored consent currently reaches, asked of the API rather than replayed from disk, because a bank can drop an account from a live consent. The uid field is what get_transactions takes.

輸入參數label

list_aspsps

List banks

List the banks Enable Banking can reach, by two-letter country code. The name field is what start_auth takes, psuTypes says whether the bank exposes personal accounts, business accounts or both, and maxConsentDays is how long that bank will let a consent last. Pass an empty country for every bank in every market. Served from a one-hour cache.

輸入參數country必填

list_stored_sessions

List stored consents

Every stored consent: its label, the bank, the accounts it covered, and when it EXPIRES (date and days left). A lapsed consent needs the account holder to approve a new one in the bank app, so check this first when a statement suddenly fails, and before one runs out.

無需輸入參數。

revoke_session

Revoke a bank consent

End a stored consent: Enable Banking is asked to close the session, which ends the bank's consent, and the label is removed from the stored consents (the file as it was is kept as a backup). Afterwards nothing can read those accounts until the account holder approves a new consent in their bank, so use it only when access should really stop (an account closed, an application retired, access no longer wanted). A consent the bank already ended is removed just the same. The label is required, always.

輸入參數label必填

start_auth

Begin a bank consent

Begin a consent with one bank, valid for 180 days, over every account the bank offers. To renew a stored consent, pass the bank, country and psu_type list_stored_sessions shows for it. Returns a URL a HUMAN must open: the bank requires strong customer authentication, so this step cannot be automated. After approval the bank redirects to the configured redirect_url with ?code=... in the query; that URL, or just the code, goes into complete_auth. Moves money or makes a legal document: the client asks the user to approve each call.

輸入參數statepsu_type必填aspsp_name必填aspsp_country必填

版本歷史

2
  1. v0.1.1最新Oct 31, 2026
  2. v0.1.0Oct 26, 2026

評分

已滿足適用檢查項的 90%

該評分衡量此收錄條目的完整程度,依據註冊中心條目、端點與公開來源計算得出。它不是對伺服器本身的評價,也不表示伺服器安全或目前可用:可連通性是帶日期的檢查結果,評分也不包含安裝量或使用者評價。

  • 近 30 天內可連通25 分
  • 已宣告認證方式10 分

    請在註冊中心條目中宣告憑證請求標頭,或保持端點能夠回應探測,以便判定其認證方式。

  • 已列出工具必需15 分
  • 描述必需15 分
  • 圖示10 分
  • 文件15 分
  • 授權條款10 分