Skip to main content
Glama
b3-dfp

b3-dfp

by msperlin

b3-dfp

An MCP server for the annual (DFP) and quarterly (ITR) financial statements of Brazilian listed companies, built for LLM agents.

It reads CVM's open data (dados.cvm.gov.br) directly — the same source behind the R package GetDFPData2 — and turns 300 MB of semicolon-separated Latin-1 CSVs into fast, token-efficient queries.

Why this server

  • No R dependency. Runs anywhere Python 3.10+ runs.

  • Official MCP SDK. Tools, resources and a prompt over stdio or HTTP.

  • Parquet + predicate push-down. Each (document, format, year) archive is parsed once into Parquet; queries filter by company and account before anything is materialized in Python. A 17-year query takes milliseconds after the first build.

  • Handles CVM's quirks. Latin-1 encoding, decimal commas, unbalanced quotes inside account labels, the 14/15/16-column schema drift across years, and amended filings (VERSAO).

  • Token-efficient output. CSV, headline-account summaries, and single-account time series instead of dumping whole statements into the context window.

  • Quarterly income statements. ITR files report year-to-date figures; the server can difference them into quarter-only numbers, deriving Q4 from the annual DFP.

Related MCP server: cvm-mcp

Tools

Tool

What it does

search_company

Name → cd_cvm. Accent-insensitive. Entry point for everything.

list_companies

Browse the registry by sector and situation.

list_available_years

Which years CVM publishes, and what is cached locally.

get_tickers

Ticker (PETR4) → cd_cvm + company, from CVM's FCA form.

get_statement_summary

Headline accounts (assets, equity, revenue, EBIT, net income) for one company-year.

get_account_history

One account across years — the smallest useful answer.

get_financial_statements

Full long-form table, capped by max_rows.

list_account_codes

Curated account codes and role names.

Resources: b3dfp://accounts, b3dfp://companies/{cvm_code}/profile. Prompt: analyze_company.

Install

Requires Python 3.10+ and uv.

git clone git@github.com:msperlin/b3-dfp.git
cd b3-dfp
uv sync

Run it over stdio:

uv run b3-dfp

Or install it as a tool:

uv tool install .
b3-dfp

Register with OpenCode

Add to ~/.config/opencode/opencode.json (or a project opencode.json):

{
  "$schema": "https://opencode.ai/config.json",
  "mcp": {
    "servers": {
      "b3-dfp": {
        "type": "local",
        "command": [
          "uv",
          "run",
          "--directory",
          "/home/msperlin/GitRepo/mcp-servers/b3-dfp",
          "b3-dfp"
        ]
      }
    }
  }
}

See opencode.example.json.

Usage examples

Ask the agent:

  • "What was WEG's net income in 2023?" → search_company("weg") → get_statement_summary(5410, 2023)

  • "Plot Petrobras' revenue, EBIT and net income from 2019 to 2024." → get_account_history([9512], "revenue", first_year=2019, last_year=2024)

  • "Compare Itaú and Bradesco's total assets." → get_account_history([19348, 906], "total_assets", doc_type="BPA")

  • "Give me the quarterly income statement of Vale for 2023." → get_financial_statements([4170], 2023, 2023, data_type="itr", doc_type="DRE", format="ind", quarterly=True)

Configuration

Variable

Default

Meaning

B3DFP_CACHE_DIR

~/.cache/b3-dfp

Where zips, Parquet and the registry live.

B3DFP_REFRESH_HOURS

12

How long a downloaded archive is considered fresh. 0 disables revalidation.

MCP_TRANSPORT

stdio

stdio, streamable-http or sse.

MCP_HOST / MCP_PORT

127.0.0.1 / 8000

Bind address for HTTP transports.

Disk usage: one year of DFP is a 13-25 MB download; the documents you actually query are then cached as tens of megabytes of Parquet. Only the documents you ask for are built, and the cache lives outside the repository.

Performance (measured)

From an empty cache, on a normal connection:

Operation

Time

search_company (registry download, 1.5 MB)

0.45 s

get_statement_summary (2023 DFP, 3 documents)

2.5 s

get_account_history, 3 years

3.0 s

Same queries, cache warm

0.01–0.05 s

The full scripts/smoke_test.py run, including every download it needs, takes about 7 seconds from scratch and leaves a 42 MB cache.

Data notes

  • Values. VL_CONTA is the number exactly as published, usually in thousands (ESCALA_MOEDA = MIL). The tools also return VL_CONTA_BRL / value_brl, already multiplied out, which is what you want to quote.

  • Account codes. CD_CONTA follows CVM's standardized plan: 1 assets, 2 liabilities and equity, 3.xx income statement, 6.xx cash flow, 7.xx value added. The meaning of a code depends on the industry chart (bank, insurer, industrial), so DS_CONTA — always returned — is authoritative. Role names (revenue, net_income, equity, ...) are resolved from the label rather than the code alone, so they behave consistently across charts: Itaú's net income is 3.09, Bradesco's is 3.11, and neither is reported as EBIT (banks have no such line).

  • Consolidated vs individual. con is the consolidated statement (default), ind is the parent-only statement.

  • Amended filings. When a company files the same account twice, the highest VERSAO wins.

  • ITR is not one number per account. Quarterly filings publish both a year-to-date column and a quarter-only column for the same account and date. The period column (ytd, quarter, annual, snapshot) tells them apart, and quarterly=True differences the year-to-date figures (Q4 comes from the annual DFP, since no ITR covers it).

  • Freshness. CVM republishes the yearly archives during the filing season; the server revalidates with If-Modified-Since instead of re-downloading.

Development

uv sync
uv run pytest -q                      # 66 unit tests, no network
uv run python scripts/smoke_test.py   # real MCP handshake + live CVM queries
uv run python scripts/parity_check.py # cell-by-cell diff against GetDFPData2 (needs R)

Correctness

scripts/parity_check.py runs the R package GetDFPData2 and this server on the same filings, then compares every row. It currently matches exactly — same row count, same account codes, same labels, same values — for Banco do Brasil (bank chart), BRF (a filing with an unbalanced quote inside DS_CONTA) and Vale in 2023, across BPA, BPP and DRE: 609 rows, zero differences.

License

MIT. Data is public and provided by CVM; see https://dados.cvm.gov.br/dados/CIA_ABERTA/DOC/DFP/.

Related MCP Connectors

Related MCP Servers

  • A
    license
    A
    quality
    B
    maintenance
    Provides access to SEC EDGAR financial data, enabling AI agents to fetch company filings, financial metrics, and narrative sections. It supports natural-language metric searching and extracts structured data from 10-K, 10-Q, and 8-K reports.
    6
    38 PyPI
    MIT
  • A
    license
    A
    quality
    C
    maintenance
    MCP server that fetches Brazilian financial data from the CVM open data portal and exposes tools for LLM clients to query companies and calculate financial indicators from published financial statements.
    4
    MIT
  • A
    license
    Not graded
    quality
    B
    maintenance
    Brazilian company-registry lookup via Receita Federal, allowing AI agents to query CNPJ data.
    420 npm
    MIT