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)
fintsrecorded · fictional data
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.

fintsrecorded · fictional data
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 EUR
Recorded with the shipped CLI and exporters against a stand-in bank (the trick fints-cli's tests use). IBANs, names and amounts are invented.
MCPwhat the agent sees from fints mcp
  1. get_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."
    }
  2. 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
    }
The real MCP server with the same stand-in bank: the first call returns at once, the second after the approval.

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 | manual per 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.

  1. 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.
  2. Send it to the address given there. Registration is free.
  3. The number usually arrives within 10–15 working days.
  4. 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.

Compared with other ways to read a German bank account
OptionKindMoves moneyAgent / scriptingThird party sees data
fints-cliCLI + MCP serverNo, by designCLI, JSON, MCPNo, direct to your bank
python-fintsPython libraryCan (you write the code)You build itNo
Hibiscus (Jameica)Desktop banking appYesPlugins, GUI firstNo
AqBankingC library + CLIYesCLINo
Aggregator APIs (PSD2)Hosted REST APIDependsRESTYes, 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.

€39once
  • 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
On sale soon · €39

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.