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-Authenticateheader 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:readscope. 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.