MCP server

Connect a model to your statements. Four read-only tools over the same endpoints as the REST API. Log in to connect, or use an API key.

https://www.extractbankstatements.com/api/mcp

MCP is how a model reaches a service directly, rather than being handed a file and asked to read it. Connect this one and you can ask questions of your statements in the place you are already working: what you spent somewhere last month, whether a statement reconciled, which rows need looking at.

It is the same product as the REST API, calling the same code. A number that comes back through a tool is the number the API returns.

Included in Pro and Business, like the API.

Connect it#

Two ways in, and the first is the one you want.

Log in#

Give a client that speaks OAuth the URL above and nothing else. It will send you here to sign in and approve it. There is nothing to copy, nothing to paste into a config file, and nothing sitting in one afterwards.

https://www.extractbankstatements.com/api/mcp

The client registers itself, asks for the statements:read scope, and gets a token bound to this endpoint alone. If you connect a second time it remembers that you already agreed.

In Claude Code:

claude mcp add --transport http extractbankstatements \
  https://www.extractbankstatements.com/api/mcp

An API key#

For a client that does not speak OAuth, and for a script driving MCP directly. The same key the REST API takes, from your API keys page, as a bearer token.

claude mcp add --transport http extractbankstatements \
  https://www.extractbankstatements.com/api/mcp \
  --header "Authorization: Bearer sk_live_..."

Or in a JSON config:

{
  "mcpServers": {
    "extractbankstatements": {
      "type": "http",
      "url": "https://www.extractbankstatements.com/api/mcp",
      "headers": {
        "Authorization": "Bearer sk_live_..."
      }
    }
  }
}

The transport is Streamable HTTP and the server is stateless, so there is no session to keep alive and nothing to run locally.

The tools#

Tool What it does
list_documents Your documents, newest first, with whether each one reconciled. Takes an optional limit, up to 100.
get_document One document: period, currency, opening and closing balance.
get_transactions Every transaction on a statement, in order, with the statement it came from.
export_document The document as CSV, for when you want the rows rather than an answer about them.

Every tool takes the document id that list_documents returns.

What a model should do with the answers#

The server tells the model this when it connects, and it is worth knowing yourself, because it is the difference between this product and a converter.

Gate on reconciled, not on status. A statement can finish processing without being provable. reconciled: true means every figure was checked against the statement's own running balance and its declared totals, and they agreed. false means the numbers are there and were not proved.

A row with needs_review: true was not proved. Reconciliation could not account for it, and review_reason says why. It should be surfaced, not quietly summed over.

Amounts and balances are strings. "-25.63", not -25.63. They are exact decimal values, and 0.1 does not survive a binary float. A model that turns them into numbers to add them up has reintroduced the error this product exists to catch.

Authentication#

Either an OAuth access token, obtained by the flow above, or an API key as Authorization: Bearer sk_live_…. Both resolve to the same account and reach the same tools.

An OAuth token is narrower than a key, which is the argument for preferring it. It carries the statements:read scope and an audience naming this endpoint, so it is useless anywhere else, and you can withdraw a client's access without touching anything else you have connected. A key is one credential for the whole API.

Two failures are worth telling apart:

  • 401 means we do not know who you are: no credential, or one that is revoked or wrong. The response carries a WWW-Authenticate header naming where to authenticate, which is how an OAuth client starts the flow without being told the address.
  • 403 means we know who you are and something about the request is not allowed: usually a plan without the API, sometimes a token missing the statements:read scope. Retrying with the same credential will not help. A plan is fixed on the billing page; a scope is fixed by reconnecting.

Reading, not writing#

There is no upload tool, on purpose.

A statement is a multi-megabyte PDF on your disk, and MCP has no good way to move one yet. Base64 in a tool argument fills the context window with a file the model cannot read. A local path means nothing to a server running in Germany. A presigned URL needs the client to upload a file it was never told to open.

The file uploads working group is drafting the missing piece: a declarative file input, so a host can show you a normal file picker and send what you choose. When that ships, uploading arrives here.

Until then: upload in the app, which takes a moment and happens once per statement, then read from wherever you are working.

Discovery#

The server describes itself, so a client that supports discovery does not need any of the above typed in.

GET /.well-known/mcp/server-card.json

The OAuth documents are at /.well-known/oauth-authorization-server and /.well-known/oauth-protected-resource/api/mcp, which is where the WWW-Authenticate header on a 401 points.

Every page also advertises the server in a Link header, alongside the API catalog and the OpenAPI description.