sofatools / fints-cli
Your German bank account, in your terminal. Read-only.
A command line tool and MCP server for German bank accounts via FinTS/HBCI: accounts, balances and transactions, straight from your bank to your terminal, your spreadsheet, your ledger or your AI agent. It cannot move money; there is no code for it.
- FinTS 3.0 PIN/TAN
- Decoupled TAN
- CSV · JSON · Ledger · Beancount
- MCP server (stdio)
fints balanceWaiting for approval in your banking app: Please confirm the request in your banking app.DE00123456780004711000 1873.55 EUR (2026-10-22)DE00123456780004711001 6200.00 EUR (2026-10-22)The problem
FinTS is there. The glue around it is the work.
German banks speak FinTS, and python-fints is a solid library for it. Between the library and “give me last month’s transactions as Beancount” lie TAN mechanisms, approvals in the banking app that you have to poll for, PSD2’s regular re-authentication, session handling so you do not approve every call, and exports. Aggregator APIs solve that by putting a third party between you and your bank.
What fints-cli does
fints-cli is that glue, done once. It talks to your bank’s FinTS server directly, waits for your approval in the app (decoupled TAN, e.g. SecureGo plus) or asks for a TAN to type, keeps the session so later calls usually need no approval, and prints what you asked for. The MCP server never blocks on an approval: it answers “confirm in your banking app, then call again” while the bank request waits in the background.
Demo
Approve once in the app, then export
The first call waits for your approval in the banking app; after that the session cache carries the next calls. On the right the MCP server, which never blocks: it says “approve and call again” while the bank request waits in the background.
fints balanceWaiting for approval in your banking app: Please confirm the request in your banking app.DE00123456780004711000 1873.55 EUR (2026-10-22)DE00123456780004711001 6200.00 EUR (2026-10-22)fints transactions DE00123456780004711000 --since 2026-09-01 --until 2026-09-30booking_date,value_date,amount,currency,counterparty,counterparty_iban,purpose,reference,iban,posting_text2026-09-01,2026-09-01,-890.00,EUR,Hausverwaltung Beispiel,DE00123456780000000011,Miete September Whg 3,MIETE-2026-09,DE00123456780004711000,DAUERAUFTRAG2026-09-02,2026-09-02,-12.40,EUR,Example Hosting,DE00123456780000000022,INV-2026-0142,EH-0142,DE00123456780004711000,LASTSCHRIFT2026-09-05,2026-09-05,-54.90,EUR,Stadtwerke Musterstadt,DE00123456780000000033,Abschlag Strom 09/2026,SWM-77812,DE00123456780004711000,LASTSCHRIFT2026-09-15,2026-09-15,-4.20,EUR,Bäckerei Krume,,Kartenzahlung,POS-30311,DE00123456780004711000,KARTENZAHLUNG2026-09-29,2026-09-29,2450.00,EUR,Beispiel GmbH,DE00123456780000000044,Gehalt September 2026,LOHN-2026-09,DE00123456780004711000,LOHN/GEHALTfints transactions --since 2026-09-01 --until 2026-09-30 --format beancount --account "Assets:Bank:{bank}" | head -92026-09-01 * "Hausverwaltung Beispiel" "Miete September Whg 3" reference: "MIETE-2026-09" Assets:Bank:MYBANK -890.00 EUR Expenses:Unknown 890.00 EUR 2026-09-02 * "Example Hosting" "INV-2026-0142" reference: "EH-0142" Assets:Bank:MYBANK -12.40 EUR Expenses:Unknown 12.40 EURfints mcpget_transactions(since: "2026-09-28", until: "2026-09-30")before the approval{ "status": "approval_needed", "message": "Approval needed: confirm the request in your banking app, then call this tool again." }get_transactions(since: "2026-09-28", until: "2026-09-30")after confirming in the app{ "transactions": [ { "iban": "DE00123456780004711000", "booking_date": "2026-09-29", "value_date": "2026-09-29", "amount": "2450.00", "currency": "EUR", "counterparty": "Beispiel GmbH", "counterparty_iban": "DE00123456780000000044", "purpose": "Gehalt September 2026", "reference": "LOHN-2026-09", "posting_text": "LOHN/GEHALT" } ], "count": 1 }
What’s included
fints accounts,balance,transactions(--since,--until,--format csv|json|ledger|beancount),login- Decoupled TAN: polls the bank for up to five minutes until you confirm in the app; a TAN to type works as a fallback;
tan = auto | decoupled | manualper bank - Session cache (file mode 0600), so you do not approve every single call
- Posting text (Buchungstext) from the bank’s CAMT data as its own field, and Ledger and Beancount output with account-name templates
- MCP server (stdio) with
list_accounts,get_balance,get_transactions, all read-only, never blocking on an approval - PIN only from an environment variable or a command such as
pass show; never stored, logged or echoed - README in English and German, with the product registration explained step by step
What it does not do
- Transfers, standing orders or anything else that moves money. Not as an option, and no code for it.
- Securities accounts, credit cards over FinTS, PDF statements, a graphical interface.
- A FinTS product ID: you register your own (free, see below).
You need
- Python 3.12+ and uv
- An account at a bank that offers FinTS 3.0 with PIN/TAN, and online banking enabled
- Your own FinTS product ID from the Deutsche Kreditwirtschaft (free registration)
Status
What has been tested
Current release: 0.1.0.
Tested with GLS Bank (Atruvia platform, approval in the SecureGo plus app): balances and transactions, including the posting text from the bank’s CAMT data. No other bank has been tested yet; see the bank list.
The TAN loop, the session cache and the exports are also covered by automated tests.
Before you buy
You need your own FinTS product ID
Since August 2019, banks only accept registered client programs over FinTS. The registration number is tied to the person or company that registered it, so fints-cli cannot ship one. Without your own number, fints-cli will not connect.
- Fill in the form on fints.org (Deutsche Kreditwirtschaft): product name, your name and address, a short description such as “private read-only account access”. The page is in German.
- Send it to the address given there. Registration is free.
- The number usually arrives within 10–15 working days.
export FINTS_PRODUCT_ID=…– done.
Alternatives
Compared with other ways to read a German bank account
If you are happy writing Python, python-fints alone gets you there; fints-cli is built on it and saves you the TAN, session and export plumbing. If you want a full banking app, take Hibiscus.
| Option | Kind | Moves money | Agent / scripting | Third party sees data |
|---|---|---|---|---|
| fints-cli | CLI + MCP server | No, by design | CLI, JSON, MCP | No, direct to your bank |
| python-fints | Python library | Can (you write the code) | You build it | No |
| Hibiscus (Jameica) | Desktop banking app | Yes | Plugins, GUI first | No |
| AqBanking | C library + CLI | Yes | CLI | No |
| Aggregator APIs (PSD2) | Hosted REST API | Depends | REST | Yes, the provider |
Based on each project’s documentation as of October 2026. Not affiliated with any bank or the Deutsche Kreditwirtschaft.
Price
Pay once. Keep the updates.
No subscription and no seats. You buy fints-cli once and keep read access to its repository, so every later version is a git pull away.
The license key Polar sends is your proof of purchase; fints-cli never checks it and never phones home.
- Read access to the private GitHub repository
- All future updates, via git pull
- A license key as proof of purchase
- Any number of your own devices
Checkout by Polar (Polar Software, Inc.) as Merchant of Record: Polar is the seller, issues the invoice and handles sales tax/VAT. You need a GitHub account. Terms
Questions
Can it make transfers?
No. Only read-only FinTS business transactions are implemented: account list, balance, transactions. There is no code path that could submit a payment, and none will be added.
Why do I need my own product ID?
Since 2019, banks only allow FinTS access to registered client products. The registration number is tied to whoever registered it, so it cannot be shared with buyers. Registering your own is free: fill in the form on fints.org (Deutsche Kreditwirtschaft) and send it as described there; the number usually arrives within 10–15 working days. Until you have it, fints-cli will not connect, so register before you need it.
Which banks work?
Most German banks offer FinTS 3.0 with PIN/TAN, and the Volksbanken and Raiffeisenbanken on the Atruvia platform share the same servers as GLS Bank. Whether a given bank works is only known once someone has tried it; the bank pages say for each bank whether it has been tested.
How often do I have to approve in the app?
On the first call, and then only when the bank asks again: the session is cached. Under PSD2, banks require a fresh strong authentication at least every 90 days, so now and then a call asks for an approval although the cache is warm. fints login forces one on purpose.
Is it a good idea to give an AI agent my bank data?
That is your call. The MCP tools cannot move money, but they do expose balances and transactions to whatever reads their output. The PIN stays out of the MCP config: use pin_command so your password manager hands it over at runtime.
What exactly do I get when I buy?
One payment, no subscription. You get read access to the private GitHub repository sofatools/fints-cli – the full source, the documentation and every future update, which you fetch with git pull. Polar also issues a license key; it is your proof of purchase. fints-cli does not check it and never phones home.
How do I get access to the repository?
You need a GitHub account. After checkout, Polar asks you to connect it and then invites that account to the repository with read access. You install straight from the private repository (for example uv tool install git+https://github.com/sofatools/fints-cli) once git can use your GitHub login; the docs show how. There is no public package.
What does the license allow?
One purchaser, any number of your own devices and bank accounts. Not allowed: redistributing, reselling or publishing the source. The software comes as is, without warranty. Full license text.
Who sells it, and what about invoices, VAT and refunds?
The checkout is run by Polar, which acts as Merchant of Record: Polar is the seller on your invoice, calculates and remits sales tax or VAT, and handles refunds under its buyer terms. See Terms.
Where do bug reports go?
To the issue tracker of the private repository you get access to. There is no support contract and no promised response time; this is a small product made by one person.