Skip to content

Bankstatemently

by bankstatemently · remote · no sign-in to list tools · v2.1.0 · published by GitHub user bankstatemently

Convert PDF bank statements into structured transactions, accounts, and balances.

Deletes · 3Final · 6Changes · 4

22 tools: 3 can delete or cancel things, 6 do something that can't be undone, 4 change things and 9 only read.

Scanned Oct 21, 2026

4 checks matched something in the tool definitions. No finding is not a safety guarantee. How the checks work.

claude mcp add --transport http bankstatemently https://api.bankstatemently.com/mcp --header 'X-API-Key: <your-x-api-key>'

Findings

Matches of fixed checks, with the text they matched.

4 warn
  • The server marks "evaluate_benchmark" read-only (readOnlyHint: true), and its own name, description or input give the class "write".

    Matched text

    annotations: {"readOnlyHint":true,"destructiveHint":false,"openWorldHint":false}; The name "evaluate_benchmark" says "evaluate": a word for a change.

    What this check looks for
  • The server marks "aggregate" read-only (readOnlyHint: true), and its own name, description or input give the class "irreversible".

    Matched text

    annotations: {"readOnlyHint":true,"destructiveHint":false,"openWorldHint":false}; The input addresses people (input.scope.dateRange.to): what it sends cannot be taken back.

    What this check looks for
  • The server marks "group_by" read-only (readOnlyHint: true), and its own name, description or input give the class "irreversible".

    Matched text

    annotations: {"readOnlyHint":true,"destructiveHint":false,"openWorldHint":false}; The input addresses people (input.scope.dateRange.to): what it sends cannot be taken back.

    What this check looks for
  • The server marks "time_series" read-only (readOnlyHint: true), and its own name, description or input give the class "irreversible".

    Matched text

    annotations: {"readOnlyHint":true,"destructiveHint":false,"openWorldHint":false}; The input addresses people (input.scope.dateRange.to): what it sends cannot be taken back.

    What this check looks for

Tools

Open a tool for its reasons and full description. Descriptions are the publisher's own words, shown as they are.

22 tools

Deletes or cancels 3Claude Code: deny
  • dismiss_statementHide a failed, rejected, or cancelled document from future list_statements results.No findings

    Why it is in this group

    • The name "dismiss_statement" says "dismiss": a word for a change.
    • The server marks it destructive (destructiveHint: true).
    Description, as the server wrote it
    Hide a failed, rejected, or cancelled document from future list_statements results. Use this only when the user asks to clear a terminal failed/rejected/cancelled conversion from their history. This is not a delete: it marks the document dismissed and leaves stored data/artifacts untouched.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: true
    • openWorldHint: false
  • rate_statementReport how well a previously converted bank statement was parsed: submit a 1-5 rating, optionally with structured feedback at any rating.No findings

    Why it is in this group

    • Nothing in its name or description says what it does, so it is counted as a write.
    • The server marks it destructive (destructiveHint: true).
    Description, as the server wrote it
    Report how well a previously converted bank statement was parsed: submit a 1-5 rating, optionally with structured feedback at any rating. Your rating counts toward the average ratings shown on bankstatemently.com. Calling this again for the same document updates your existing rating without clearing feedback already submitted for it. Returns the stored rating state in the response — there is no separate tool to read your own rating back. Every response includes a "summary" field: use it as the single source of truth for what happened.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: true
    • openWorldHint: true
  • update_memoryRename or rescope an existing named memory, or update its situation.No findings

    Why it is in this group

    • The name "update_memory" says "update": a word for a change.
    • The input addresses people (input.scope.dateRange.to): what it sends cannot be taken back.
    • The server marks it destructive (destructiveHint: true).
    Description, as the server wrote it
    Rename or rescope an existing named memory, or update its situation. The default memory is immutable. Free to use — no credits consumed.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: true
    • openWorldHint: false
Can't be undone 6Claude Code: ask
  • adjudicate_outflowsDecide where an "unresolved" debit from list_outflows went (the documents' claims conflict and nothing settles them).No findings

    Why it is in this group

    • The input addresses people (input.dateRange.to): what it sends cannot be taken back.
    Description, as the server wrote it
    Decide where an "unresolved" debit from list_outflows went (the documents' claims conflict and nothing settles them). Pass "verdict" and "transactionIds" when the user has told you; leave "verdict" out to let the AI judge decide the named rows, or every unresolved row. Returns the updated list_outflows answer.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • openWorldHint: false
  • adjudicate_transfersDecide whether ambiguous pairs from list_transfers are transfers.No findings

    Why it is in this group

    • The name "adjudicate_transfers" says "transfer": a word for a change that another call does not undo.
    • The input addresses people (input.scope.dateRange.to): what it sends cannot be taken back.
    Description, as the server wrote it
    Decide whether ambiguous pairs from list_transfers are transfers. Pass "verdict" when the user has told you; leave it out to let the AI judge decide. Returns the updated list_transfers answer.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • openWorldHint: false
  • aggregateCompute a single metric (sum/average/count/max/min) over a filtered set of transactions across your converted statements.1 finding

    Why it is in this group

    • The input addresses people (input.scope.dateRange.to): what it sends cannot be taken back.
    • The server marks it read-only (readOnlyHint: true), but an annotation never lowers a class.

    Findings

    • Warn

      The server marks "aggregate" read-only (readOnlyHint: true), and its own name, description or input give the class "irreversible".

      Matched text

      annotations: {"readOnlyHint":true,"destructiveHint":false,"openWorldHint":false}; The input addresses people (input.scope.dateRange.to): what it sends cannot be taken back.

      What this check looks for
    Description, as the server wrote it
    Compute a single metric (sum/average/count/max/min) over a filtered set of transactions across your converted statements. Results are per-currency — never sum across currencies yourself. Scope defaults to all your completed statements; pass "scope" to narrow to specific accounts/products and/or a date range. For "how many credits do I have" / processing quota / remaining pages, use get_credits instead — that is not a transaction.

    Annotations the server declares

    • readOnlyHint: true
    • destructiveHint: false
    • openWorldHint: false
  • create_memoryCreate a named memory: a scoped lens over your accounts and dates with a user-authored name, optional situation ("bookkeeping", "divorce" or "estate"), and ChatScope.No findings

    Why it is in this group

    • The name "create_memory" says "create": a word for a change.
    • The input addresses people (input.scope.dateRange.to): what it sends cannot be taken back.
    Description, as the server wrote it
    Create a named memory: a scoped lens over your accounts and dates with a user-authored name, optional situation ("bookkeeping", "divorce" or "estate"), and ChatScope. Free to use — no credits consumed.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • openWorldHint: false
  • group_byGroup transactions by a dimension (month/category/counterparty/account/currency) and apply a metric to each group.1 finding

    Why it is in this group

    • The input addresses people (input.scope.dateRange.to): what it sends cannot be taken back.
    • The server marks it read-only (readOnlyHint: true), but an annotation never lowers a class.

    Findings

    • Warn

      The server marks "group_by" read-only (readOnlyHint: true), and its own name, description or input give the class "irreversible".

      Matched text

      annotations: {"readOnlyHint":true,"destructiveHint":false,"openWorldHint":false}; The input addresses people (input.scope.dateRange.to): what it sends cannot be taken back.

      What this check looks for
    Description, as the server wrote it
    Group transactions by a dimension (month/category/counterparty/account/currency) and apply a metric to each group. Results are per-currency. The "counterparty" dimension keys on the printed counterparty, else a printed account mask (••1234), else the description's name tokens, so a rail word or reference number never splits one payee; a line with no key groups under "unknown". Scope defaults to all your completed statements; pass "scope" to narrow to specific accounts/products and/or a date range.

    Annotations the server declares

    • readOnlyHint: true
    • destructiveHint: false
    • openWorldHint: false
  • time_seriesCompute a time series by grouping transactions into week or month buckets and applying a metric — useful for trends.1 finding

    Why it is in this group

    • The input addresses people (input.scope.dateRange.to): what it sends cannot be taken back.
    • The server marks it read-only (readOnlyHint: true), but an annotation never lowers a class.

    Findings

    • Warn

      The server marks "time_series" read-only (readOnlyHint: true), and its own name, description or input give the class "irreversible".

      Matched text

      annotations: {"readOnlyHint":true,"destructiveHint":false,"openWorldHint":false}; The input addresses people (input.scope.dateRange.to): what it sends cannot be taken back.

      What this check looks for
    Description, as the server wrote it
    Compute a time series by grouping transactions into week or month buckets and applying a metric — useful for trends. Scope defaults to all your completed statements; pass "scope" to narrow to specific accounts/products and/or a date range.

    Annotations the server declares

    • readOnlyHint: true
    • destructiveHint: false
    • openWorldHint: false
Changes things 4Claude Code: ask
  • categorize_statementRun AI transaction categorization on a previously processed document, then return its category mappings.No findings

    Why it is in this group

    • The description's first verb is "run": a word for a change.
    Description, as the server wrote it
    Run AI transaction categorization on a previously processed document, then return its category mappings. Returns cached categories with no charge if this document was already categorized. Consumes credits (pooled per page, same rate as the categorize toggle on the website) the first time — free on every re-fetch after. Every response includes a "summary" field: use it as the single source of truth for what happened.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • openWorldHint: false
  • convert_statementConvert a bank statement PDF into structured data or a spreadsheet.No findings

    Why it is in this group

    • The name "convert_statement" says "convert": a word for a change.
    Description, as the server wrote it
    Convert a bank statement PDF into structured data or a spreadsheet. When the user attaches a PDF in the conversation, it arrives automatically as pdf_file — never encode it yourself. Otherwise, pass pdf_url for a public HTTPS link. If your host has no way to reference the attached file at all (no pdf_file/pdf_url equivalent), call request_upload first and pass its upload_id here instead. The base64 pdf parameter is a last resort only, for a caller with no other way to reference the file. To convert several statements in one call, pass upload_ids (the array from a single request_upload call made with count set) instead of pdf/pdf_url/pdf_file/upload_id — mutually exclusive with those four. This batch form only ADMITS each file (queues it, or reports an already-completed duplicate) and returns immediately with a compact per-file status list plus a summary — it never waits for conversion, so call get_statement per document_id once ready rather than expecting inline results here. Returns accounts, transactions, and metadata. output_format "json" (default) returns the data inline, renderable in chat. The other formats (csv, xlsx, qbo, xero) return a time-limited download link instead: present it as a normal link. Every response includes a "summary" field: use it as the single source of truth for what happened. If the conversation is not in English, translate it faithfully into the conversation language; never add details it doesn't contain. Never echo raw status values (e.g. "completed") or field names. Consumes credits (1 per page). Page limit depends on your plan.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • openWorldHint: true
  • evaluate_benchmarkScore parsed bank statement transactions against the Bankstatemently benchmark ground truth.1 finding

    Why it is in this group

    • The name "evaluate_benchmark" says "evaluate": a word for a change.
    • The server marks it read-only (readOnlyHint: true), but an annotation never lowers a class.

    Findings

    • Warn

      The server marks "evaluate_benchmark" read-only (readOnlyHint: true), and its own name, description or input give the class "write".

      Matched text

      annotations: {"readOnlyHint":true,"destructiveHint":false,"openWorldHint":false}; The name "evaluate_benchmark" says "evaluate": a word for a change.

      What this check looks for
    Description, as the server wrote it
    Score parsed bank statement transactions against the Bankstatemently benchmark ground truth. Accepts a statement_id (e.g. "bsb-001") or content_hash, plus your parsed transactions. Returns extraction accuracy, integrity score, and an overall score. Only statements marked published: true in the catalog can be evaluated — held-out statements return an error. transactions[].originalData is optional but strongly recommended: fetch it via get_statement with data_mode: "original" and pass it through verbatim — an absent originalData scores that transaction's raw-fidelity (parsed) dimension 0; never fabricate a value. Free to use — no credits consumed. Read the benchmark://catalog resource first to see available statements and their published status.

    Annotations the server declares

    • readOnlyHint: true
    • destructiveHint: false
    • openWorldHint: false
  • request_uploadMint a single-use upload URL for pushing a conversation-attached PDF to Bankstatemently before converting it.No findings

    Why it is in this group

    • The name "request_upload" says "request": a word for a change.
    Description, as the server wrote it
    Mint a single-use upload URL for pushing a conversation-attached PDF to Bankstatemently before converting it. Use this ONLY when you have no other way to reference the attached file (no pdf_file/pdf_url equivalent for this host) — e.g. a code-execution sandbox that can see the file on disk but has no URL for it. Playbook: (1) check your sandbox's uploads/attachments directory first — if the file isn't there yet, the mount can lag behind the conversation; ask the user to re-attach or wait a moment and check again before calling this tool. (2) Call request_upload to get upload_url and upload_id. (3) PUT the raw PDF bytes to upload_url with header Content-Type: application/pdf, e.g.: `curl -X PUT "<upload_url>" -H "Content-Type: application/pdf" --data-binary @<path-to-file>`. (4) Once the PUT succeeds, call convert_statement with upload_id set to the same value — never pdf/pdf_url/pdf_file for this flow. The URL and token are single-use and expire quickly; call request_upload again for a fresh one if the PUT fails partway through — never retry a failed PUT against the same URL. If the PUT fails with a network error or a "host not allowed"-style denial, the sandbox is likely blocking outbound requests to api.bankstatemently.com — tell the user to add api.bankstatemently.com to their host's code-execution allowed-domains setting (on claude.ai: Settings → Capabilities → Code execution) and retry. To convert several statements at once, pass count (1-100) instead of calling this tool once per file: the response returns "uploads", an array of that many { upload_id, upload_url } pairs — PUT each file to its own upload_url, then make ONE convert_statement call with upload_ids set to every upload_id. Free to use — no credits consumed (conversion itself still costs credits, same as any other convert_statement call).

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • openWorldHint: false
Only reads 9Claude Code: allow
  • compareSide-by-side metric comparison for two filtered groups of transactions (e.g.No findings

    Why it is in this group

    • The name "compare" says "compare": a word for reading.
    Description, as the server wrote it
    Side-by-side metric comparison for two filtered groups of transactions (e.g. one category vs another, one month vs another). Scope defaults to all your completed statements; pass "scope" to narrow to specific accounts/products and/or a date range.

    Annotations the server declares

    • readOnlyHint: true
    • destructiveHint: false
    • openWorldHint: false
  • get_creditsYour remaining Bankstatemently credits — the processing quota, NOT credit/debit transactions.No findings

    Why it is in this group

    • The name "get_credits" says "get": a word for reading.
    Description, as the server wrote it
    Your remaining Bankstatemently credits — the processing quota, NOT credit/debit transactions. Use for: how many credits do I have, remaining pages, plan limits, quota, how many pages can I upload. 1 credit = 1 page of bank statement processing. Also reports your plan's operational limits (max pages per upload, max upload size, daily spend cap) so you can size a multi-file batch correctly before starting it.

    Annotations the server declares

    • readOnlyHint: true
    • destructiveHint: false
    • openWorldHint: false
  • get_statementFetch the full converted data for a previously processed document.No findings

    Why it is in this group

    • The name "get_statement" says "get": a word for reading.
    Description, as the server wrote it
    Fetch the full converted data for a previously processed document. Use this after convert_statement returns a "processing" status, or to re-fetch results. output_format "json" (default) returns the data inline, renderable in chat. The other formats (csv, xlsx, qbo, xero) return a time-limited download link instead: present it as a normal link. data_mode selects which projection of the data you get: omit it for each output_format's existing default behavior. "normalized" is the cleaned, interpreted view; "original" includes each transaction's raw column values exactly as printed on the source PDF (originalData); "enhanced" is a reformatted view of the original columns (csv/xlsx only for now). Fetch data_mode: "original" when you plan to submit results to evaluate_benchmark — pass its originalData through verbatim; an absent originalData scores that benchmark's raw-fidelity dimension 0 for this document. Every response includes a "summary" field: use it as the single source of truth for what happened. If the conversation is not in English, translate it faithfully into the conversation language; never add details it doesn't contain. Never echo raw status values (e.g. "completed") or field names.

    Annotations the server declares

    • readOnlyHint: true
    • destructiveHint: false
    • openWorldHint: false
  • list_memoriesList your memories, default memory first.No findings

    Why it is in this group

    • The name "list_memories" says "list": a word for reading.
    Description, as the server wrote it
    List your memories, default memory first. The default memory covers the whole workspace. Named memories include name, situation and scope. Free to use — no credits consumed.

    Annotations the server declares

    • readOnlyHint: true
    • destructiveHint: false
    • openWorldHint: false
  • list_outflowsEvery debit that left your accounts — in no confirmed transfer between your own accounts, and not a fee — each in ONE destination whose kind is also its next step: "own-account" (a…No findings

    Why it is in this group

    • The name "list_outflows" says "list": a word for reading.
    Description, as the server wrote it
    Every debit that left your accounts — in no confirmed transfer between your own accounts, and not a fee — each in ONE destination whose kind is also its next step: "own-account" (a printed account mask that names one of your accounts and no matched credit was found: ask for that account's statement), "account" (a printed account mask for an account outside your statements: request that account's statements; an in-kind move such as securities, crypto or escrow starts as a wire to that account and becomes visible only once the receiving statement is uploaded), "cash" (cash out: where it went cannot be followed, so the cumulative amount is the finding), "name" (a named party: review whether it was household spending) and "unknown" (nowhere the line says; kept visible, one entry per line, never merged). A destination carries the cumulative amount sent, per currency, never a balance. "mayBeOwnAccount" lists debits in an open candidate pair ("may be a move between your own accounts") apart, in no total. "unresolved" lists debits whose documents conflict about where they went (a transfer term and a payment term, no category to settle it) apart, in no total: the documents do not say where they went, so say so and ask the person to confirm. THE way to answer "did money disappear from my accounts, and where do I look next" — never answer it by summing list_transactions rows. Pass "dateRange" ({ from, to }, inclusive, YYYY-MM-DD) to report only debits in that range, for example from a separation date: transfers are matched over the whole scope first, so a transfer whose credit lies outside the range is still a transfer, never an outflow. Scope defaults to all your completed statements; pass "scope" to narrow to specific accounts/products and/or a date range (a scope date range also narrows what is matched — prefer "dateRange").

    Annotations the server declares

    • readOnlyHint: true
    • destructiveHint: false
    • openWorldHint: false
  • list_statementsBrowse your previously converted bank statements with pagination and optional status filter.No findings

    Why it is in this group

    • The name "list_statements" says "list": a word for reading.
    Description, as the server wrote it
    Browse your previously converted bank statements with pagination and optional status filter.

    Annotations the server declares

    • readOnlyHint: true
    • destructiveHint: false
    • openWorldHint: false
  • list_transactionsA transaction is a single line as printed on one account's statement — one side of any movement.No findings

    Why it is in this group

    • The name "list_transactions" says "list": a word for reading.
    Description, as the server wrote it
    A transaction is a single line as printed on one account's statement — one side of any movement. Return a filtered list of transactions across your converted statements, capped at 50 rows. Scope defaults to all your completed statements; pass "scope" to narrow to specific accounts/products and/or a date range. Every response names the scope it actually evaluated (document count + covered date range) and each returned row carries its source document's content_hash so you can cite it. For "how many credits do I have" / processing quota / remaining pages, use get_credits instead — that is not a transaction.

    Annotations the server declares

    • readOnlyHint: true
    • destructiveHint: false
    • openWorldHint: false
  • list_transfersMatch transfers between your own accounts.No findings

    Why it is in this group

    • The name "list_transfers" says "list": a word for reading.
    Description, as the server wrote it
    Match transfers between your own accounts. A transfer is TWO transactions — a debit leaving one of your accounts and a credit arriving in another — matched as two sides of the same movement (amount and date aligned); account-level successions (an account closing into a successor) are matched too. A payment to an outside party is not a transfer here: only movements with both sides visible in your statements are matched. THE way to answer any "was money moved between my accounts" / "did I transfer X" question — never try to answer a money-moved-between-accounts question with list_transactions + arithmetic; always call this tool instead. Ties come back as "ambiguous" until decided; call adjudicate_transfers to decide them. Scope defaults to all your completed statements; pass "scope" to narrow to specific accounts/products and/or a date range. Every response reports the match window (in days) it used, even when no transfers are found — a lack of matches is never silent about how hard it looked. To find large movements with NO matching counterpart in your other accounts — e.g. "trace transfers over $10,000; which ones leave without a known destination?" — pass "amountMin": reconciled pairs and successions are filtered to that floor, and the response gains an "unmatched" bucket of large movements (debits leaving, or unexplained credits arriving) with no matching pair, candidate, or succession. Omit amountMin for the ordinary reconciled-pairs answer.

    Annotations the server declares

    • readOnlyHint: true
    • destructiveHint: false
    • openWorldHint: false
  • top_nReturn the top N groups ranked by metric (descending), per-currency for monetary metrics.No findings

    Why it is in this group

    • The description's first verb is "return": a word for reading.
    Description, as the server wrote it
    Return the top N groups ranked by metric (descending), per-currency for monetary metrics. Scope defaults to all your completed statements; pass "scope" to narrow to specific accounts/products and/or a date range.

    Annotations the server declares

    • readOnlyHint: true
    • destructiveHint: false
    • openWorldHint: false

This is not a safety rating, and no finding is not a safety guarantee. The groups and findings come from fixed checks of the tool definitions the server returned on the date shown, not from its code or what it does when called. How the checks work.

Install

Pinned to version 2.1.0. Values in angle brackets are yours to fill in.

claude mcp add --transport http bankstatemently https://api.bankstatemently.com/mcp --header 'X-API-Key: <your-x-api-key>'

Run in a terminal.

Claude Code permissions

Allow the reads, ask before a change, deny the deletes. Paste into .claude/settings.json.

.claude/settings.json
{
  "permissions": {
    "allow": [
      "mcp__bankstatemently__get_statement",
      "mcp__bankstatemently__list_statements",
      "mcp__bankstatemently__get_credits",
      "mcp__bankstatemently__list_memories",
      "mcp__bankstatemently__list_transactions",
      "mcp__bankstatemently__top_n",
      "mcp__bankstatemently__compare",
      "mcp__bankstatemently__list_transfers",
      "mcp__bankstatemently__list_outflows"
    ],
    "ask": [
      "mcp__bankstatemently__request_upload",
      "mcp__bankstatemently__convert_statement",
      "mcp__bankstatemently__categorize_statement",
      "mcp__bankstatemently__adjudicate_transfers",
      "mcp__bankstatemently__adjudicate_outflows",
      "mcp__bankstatemently__create_memory",
      "mcp__bankstatemently__evaluate_benchmark",
      "mcp__bankstatemently__aggregate",
      "mcp__bankstatemently__group_by",
      "mcp__bankstatemently__time_series"
    ],
    "deny": [
      "mcp__bankstatemently__dismiss_statement",
      "mcp__bankstatemently__update_memory",
      "mcp__bankstatemently__rate_statement"
    ]
  }
}

Tools that only read are allowed, tools that change things ask first, tools that delete or cancel are denied. It assumes the server is installed as bankstatemently. Review it before you use it.