cvm-mcp
An MCP server for the CVM open-data repository
(dados.cvm.gov.br) — the Brazilian securities regulator and the source behind
the R packages GetDFPData2 and GetFREData.
It gives Claude Code, OpenCode, Cursor, VS Code, Codex, Gemini CLI and any other MCP client one interface to:
- annual and quarterly financial statements (DFP / ITR)
- the Formulário de Referência (FRE, 36 corporate tables)
- fund NAVs, flows, portfolios and credit-fund reports
- insider trading, governance, public offerings and sanctioning proceedings
- ad-hoc SQL over everything, via DuckDB
15 datasets, 111 tables, 34 tools. Everything runs locally and read-only; data is downloaded on demand and cached on disk.
This server consolidates the earlier
cvm-dfp-mcpandcvm-fre-mcpservers and is a strict superset of both.
Contents
- Requirements
- Quick start
- Client setup
- Example prompts
- Datasets
- Tools
- Configuration
- Cache
- Data notes
- How it works
- Development
- Troubleshooting
- License
Requirements
- Python 3.10+ —
uvinstalls and manages it for you. - uv — the only prerequisite.
- Network access to
dados.cvm.gov.br. - Disk space for the cache. It is lazy: nothing is downloaded until you ask for
it. A full DFP + FRE history is a few GB;
fi_cdais the heavy one (~270 MB uncompressed per month).
Install uv:
# macOS / Linux
curl -LsSf https://astral.sh/uv/install.sh | sh
# Windows (PowerShell)
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"
Verify with uv --version.
Quick start
The package is on PyPI.
Run without installing (best for trying it out and for client configs):
uvx cvm-mcp
This starts a stdio MCP server and waits for a client. Seeing no output is
expected; press Ctrl+C to stop it.
Install a persistent cvm-mcp command (optional):
uv tool install cvm-mcp
cvm-mcp
If cvm-mcp is not found after installing, run uv tool update-shell and
restart your terminal.
From source (for development):
git clone https://github.com/msperlin/cvm-mcp.git
cd cvm-mcp
uv sync
uv run cvm-mcp
Client setup
All configurations below run the server over stdio. They use uvx, which
resolves and caches the package on first use.
GUI clients and
PATH: Claude Desktop, Cursor and VS Code often do not inherit your shellPATH. If the server fails to start with “command not found”, runwhich uvx(macOS/Linux) orwhere uvx(Windows) and use that absolute path ascommand— commonly~/.local/bin/uvx. Alternatively,uv tool installand use the absolute path tocvm-mcp.
Claude Code
Add it from the CLI, scoped to your user (available in every project):
claude mcp add --transport stdio --scope user cvm-mcp -- \
uvx cvm-mcp
Check the connection:
claude mcp list
Or commit a project-scoped .mcp.json to the repository root:
{
"mcpServers": {
"cvm-mcp": {
"type": "stdio",
"command": "uvx",
"args": [
"cvm-mcp"
],
"timeout": 600000
}
}
}
timeout is in milliseconds and applies per tool call. The first CVM download
can take a while; 10 minutes is a safe ceiling.
OpenCode
OpenCode V2 configures servers under mcp.servers. Add it globally with the
CLI:
opencode mcp add cvm-mcp --global -- \
uvx cvm-mcp
Check it with opencode mcp list (or /mcps inside OpenCode).
Or edit ~/.config/opencode/opencode.json (global) or opencode.json
(project):
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"servers": {
"cvm-mcp": {
"type": "local",
"command": [
"uvx",
"cvm-mcp"
]
}
}
}
}
The repository also ships a ready-to-copy
opencode.example.json.
OpenCode V1 used server names directly under
mcp; V2 usesmcp.servers.
Claude Desktop
Edit the Claude Desktop config file:
| OS | Path |
|---|---|
| macOS | ~/Library/Application Support/Claude/claude_desktop_config.json |
| Windows | %APPDATA%\Claude\claude_desktop_config.json |
| Linux | ~/.config/Claude/claude_desktop_config.json |
{
"mcpServers": {
"cvm-mcp": {
"command": "uvx",
"args": [
"cvm-mcp"
]
}
}
}
Quit Claude Desktop completely and reopen it. The server appears under Connectors / the tools menu.
Cursor
Create ~/.cursor/mcp.json for all projects, or .cursor/mcp.json in a
project:
{
"mcpServers": {
"cvm-mcp": {
"type": "stdio",
"command": "uvx",
"args": [
"cvm-mcp"
]
}
}
}
Reload the window if the server does not appear under Customize → MCP.
VS Code / GitHub Copilot
Create .vscode/mcp.json in the workspace:
{
"servers": {
"cvm-mcp": {
"type": "stdio",
"command": "uvx",
"args": [
"cvm-mcp"
]
}
}
}
VS Code uses the servers key. Portable .mcp.json files (Claude Desktop,
Cursor, Cline, etc.) use mcpServers instead — do not mix the two.
You can also run MCP: Add Server from the Command Palette and choose Command (stdio) with:
uvx cvm-mcp
Codex CLI
Add to ~/.codex/config.toml (or .codex/config.toml in a trusted project):
[mcp_servers.cvm-mcp]
command = "uvx"
args = ["cvm-mcp"]
startup_timeout_sec = 30
tool_timeout_sec = 600
Codex reads server instructions and exposes the tools automatically. Use
/mcp in a Codex session to inspect the connection.
Gemini CLI
Add to ~/.gemini/settings.json (user) or .gemini/settings.json (project):
{
"mcpServers": {
"cvm-mcp": {
"command": "uvx",
"args": [
"cvm-mcp"
],
"timeout": 600000
}
}
}
Then run /mcp inside Gemini CLI to check the connection. timeout is in
milliseconds.
Continue
Create .continue/mcpServers/cvm-mcp.yaml:
name: CVM MCP
version: 0.0.1
schema: v1
mcpServers:
- name: CVM
type: stdio
command: uvx
args:
- "cvm-mcp"
Continue can also import the JSON block from the Other MCP clients
section into .continue/mcpServers/.
Other MCP clients
Most clients accept a stdio server in this shape:
{
"mcpServers": {
"cvm-mcp": {
"command": "uvx",
"args": [
"cvm-mcp"
]
}
}
}
If your client asks for a single command line:
uvx cvm-mcp
Windows clients that cannot find uvx can use the executable installed by
uv tool install directly, e.g.
C:\Users\<you>\.local\bin\cvm-mcp.exe.
Example prompts
Once connected, ask in natural language:
- “What were Vale's revenue, EBIT and net debt in 2024? Cite the CVM/DFP accounts.”
- “Compare the last five years of ROE, net margin and net debt/EBITDA for Petrobras, Vale and Itaú.”
- “Show the board composition, compensation and shareholders of Banco do Brasil in the latest Formulário de Referência.”
- “Which insiders bought or sold WEG shares in the last six months?”
- “List the largest FIDCs by net asset value in the latest month, with delinquency and credit segments.”
- “How much did Brazilian companies raise in public offerings in 2024, by asset type and lead underwriter?”
- “Find sanctioning proceedings mentioning insider trading since 2023.”
- “Load DFP 2024 and run a SQL query joining the income statement with the company registry.”
The server is instructed to cite the dataset and reference period and never to invent figures.
Datasets
| Domain | Datasets | Coverage |
|---|---|---|
| Companies | dfp, itr, fre (36 tables), fca, vlmo (insider trading), cgvn (governance) |
DFP/ITR/FRE 2010+; FCA/VLMO/CGVN 2018+ |
| Funds | fi_inf_diario, fi_cda, fi_cad, fii_inf_mensal, fidc_inf_mensal (18 tables), fiagro_inf_mensal |
FI daily 2021-01+; CDA 2023-01+; FII 2016+; FIDC 2025-01+; FIAGRO 2025-05+ |
| Offerings | oferta (ICVM 400 / RCVM 160) |
1988+ |
| Enforcement | sancionador (PAS) |
current |
| Registry | cad_cia_aberta |
current |
Use list_datasets from a client to see the exact table names and view names.
describe_dataset shows columns and coverage; describe_dataset(detail=True)
adds units and caveats.
Tools
There are two layers.
Curated tools for the common questions:
| Group | Tools |
|---|---|
| Identity | search_company, resolve, list_companies, get_tickers |
| Statements | list_account_codes, get_financial_statements, get_account_history, get_statement_summary |
| Corporate filings | list_fre_tables, get_fre_table, get_company_overview, get_shareholders, get_compensation, get_board, get_capital |
| Insiders & governance | get_insider_trades, insider_summary, governance_practices, governance_score |
| Funds | search_fund, get_fund_nav, get_fund_flows, get_fund_portfolio, get_fidc_report, rank_funds |
| Offerings | search_offerings, issuance_volume, league_table |
Meta tools support both layers:
| Tool | Purpose |
|---|---|
list_datasets |
All datasets, tables and DuckDB view names |
describe_dataset |
Columns, cached periods and (with detail=True) column meanings |
load_dataset |
Download/parse a dataset or period without forcing a refresh |
cache_status |
What is cached, per dataset, with sizes |
refresh_cache |
Force a re-download and rebuild |
run_query |
Read-only DuckDB SQL over every cached table |
run_query sees tables as <dataset>__<table>, e.g. dfp__dre_con,
fi_inf_diario__informe, vlmo__transacoes. Only cached periods exist, so call
load_dataset first when you need a specific period.
Configuration
All settings are optional environment variables:
| Variable | Default | Meaning |
|---|---|---|
CVMMCP_CACHE_DIR |
~/.cache/cvm-mcp |
Cache root (downloads + Parquet) |
CVMMCP_REFRESH_HOURS |
12 |
Revalidation window for CVM archives; 0 disables revalidation |
MCP_TRANSPORT |
stdio |
stdio, streamable-http or sse |
MCP_HOST |
127.0.0.1 |
Host for the HTTP transports |
MCP_PORT |
8000 |
Port for the HTTP transports |
Add CVMMCP_CACHE_DIR wherever your client expects environment variables:
{
"env": {
"CVMMCP_CACHE_DIR": "/absolute/path/to/cvm-cache"
}
}
For a long-running HTTP server:
MCP_TRANSPORT=streamable-http MCP_HOST=127.0.0.1 MCP_PORT=8000 cvm-mcp
The HTTP transports have no authentication; bind to localhost or put the server behind a reverse proxy.
Cache
~/.cache/cvm-mcp/
├── downloads/ raw zips and CSVs, revalidated with If-Modified-Since
│ └── <archive>.meta.json remote Last-Modified/ETag + last revalidation time
└── parquet/v1/<dataset>/<table>/period=<YYYY|YYYYMM|all>/data.parquet
- Everything is lazy. The first call for a dataset downloads and parses the CVM archive; later calls read the Parquet partition.
- CVM rewrites the current month in place, so archives are revalidated every
12 h by default (
CVMMCP_REFRESH_HOURS). - A
304 Not Modifiedupdates only the sidecar'schecked_at; the archive mtime stays put, so the Parquet partition is reused instead of being parsed again. load_datasetpopulates the cache without forcing a refresh.refresh_cacheforces a re-download and rebuild.cache_statusreports datasets, periods, partition counts and bytes. To reclaim space, delete~/.cache/cvm-mcp/parquet(it is rebuilt on demand) or pointCVMMCP_CACHE_DIRat a larger disk.
Data notes
- DFP / ITR keep CVM's uppercase column names.
VL_CONTAis the raw value;VL_CONTA_BRLis already scaled byESCALA_MOEDAand is the number to report. - FRE keeps the GetFREData-compatible snake_case schema and key block
(
cd_cvm,cnpj,nome_companhia,data_referencia,versao,id_documento). - Newer datasets (VLMO, CGVN, funds, offerings) are normalized to snake_case and
gain a 14-digit
cnpjcolumn and a_periodcolumn. fi_cdais heavy (~270 MB uncompressed per month). Build one block/period at a time;load_datasetrefuses more than three periods of a heavy dataset in one call.
Fund family routing
CVM splits funds by instrument, not by manager, and the tools hide that.
get_fund_nav and get_fund_portfolio resolve the class CNPJ in fi_cad, read
its tipo_classe and pick the dataset:
| Family | Series | Portfolio |
|---|---|---|
| FIF / FAPI | fi_inf_diario (daily) |
fi_cda blocks 1–8 |
| FII | fii_inf_mensal (monthly) |
fii_inf_mensal/ativo_passivo |
| FIDC | fidc_inf_mensal/tab_iv |
fidc_inf_mensal/tab_ii (credit segments) |
| FIAGRO | fiagro_inf_mensal |
fi_cda/fie |
get_fidc_report adds the credit dashboard (assets, portfolio, PL, delinquency).
Known gaps: FIP is quarterly and is not wired up yet; the tools say so
explicitly. FIDC monthly reports start in 2025-01 — 2013–2024 lives in the
HIST/ folder with a different layout and is not wired up. FII history starts
in 2016.
How it works
The portal has ~1,259 files and ~10 GB of archives, but only about five shapes
of data. Each dataset is described declaratively in registry.py — URL pattern,
period granularity, tables inside the archive, normalizer — and the store turns
every (dataset, table, period) into one Parquet partition. Adding a dataset is
a data change, not a code change.
src/cvm_mcp/
├── app.py MCP application object + server instructions
├── registry.py declarative catalog of datasets and tables
├── fetch.py download, revalidation, metadata sidecars, file locks
├── store.py Parquet partitions + DuckDB views + read-only SQL
├── normalize.py Latin-1 reader, header cleaning, CNPJ, type inference
├── identity.py CNPJ ↔ cd_cvm ↔ ticker resolution
├── schema_dfp.py canonical DFP/ITR schema
├── catalog_dfp.py curated chart of accounts and roles
├── catalog_fre.py the 36 FRE tables
├── fre_normalize.py FRE normalization
├── data_dictionary.py curated column descriptions, units and caveats
├── tools_*.py the MCP tools, one module per domain
└── server.py entry point
Each archive is downloaded once and revalidated with If-Modified-Since;
downloads are guarded by a cross-process file lock, and SQL is restricted to a
single read-only SELECT / WITH over the cached views.
Development
git clone https://github.com/msperlin/cvm-mcp.git
cd cvm-mcp
uv sync
uv run pytest -m "not network" # fast, offline
uv run pytest -m network # hits dados.cvm.gov.br
uv run python scripts/smoke_mcp.py # end-to-end stdio smoke test
To run the working tree instead of the GitHub package in a client config, point the command at the checkout:
{
"command": "uv",
"args": ["run", "--directory", "/absolute/path/to/cvm-mcp", "cvm-mcp"]
}
Releasing
Publishing happens on GitHub, not from a laptop, through PyPI trusted publishing (OIDC), so no API token is stored anywhere.
-
Bump
versioninpyproject.tomland commit it. -
Tag the commit with a matching
vprefix and push it:git tag v0.1.0 git push origin v0.1.0
-
Draft a GitHub release for that tag and publish it.
The publish workflow in .github/workflows/publish.yml then runs the offline
test suite, builds the sdist and wheel, checks the metadata, verifies the tag
matches the version, and uploads to PyPI. Re-running it manually
(workflow_dispatch) rebuilds and re-uploads the current version, which PyPI
rejects if the files already exist — so only do that after a failed release.
One-time setup (already required before the first upload): on PyPI, add a
pending publisher for project cvm-mcp with owner msperlin, repository
cvm-mcp, workflow publish.yml, and environment pypi. The matching
environment is declared in the workflow.
Troubleshooting
| Symptom | Fix |
|---|---|
uvx: command not found |
Install uv, or use the absolute path from which uvx / where uvx. GUI clients often do not inherit PATH. |
| Server starts but no tools appear | Check the client's MCP panel: claude mcp list, opencode mcp list, /mcp in Gemini/Codex/Cursor/VS Code. Restart the app after editing JSON. |
| First tool call is slow | The server is downloading and parsing a CVM archive. Use load_dataset for the exact period you need, and raise the client's tool timeout. fi_cda is the heaviest dataset. |
run_query says “no data cached” |
Only cached periods are visible to SQL. Call load_dataset (e.g. dataset="dfp", period="2024") or a curated tool first. |
| The latest month looks stale | CVM rewrites the current month in place. Call refresh_cache for that dataset/period. |
| Disk is filling up | Check cache_status; delete ~/.cache/cvm-mcp/parquet (rebuilt on demand) or move CVMMCP_CACHE_DIR. |
CVM returned HTTP 5xx / timeouts |
CVM is temporarily unavailable; the server retries 429/500/502/503/504 with backoff. Check connectivity to dados.cvm.gov.br. |
| Unknown dataset/table | Call list_datasets and describe_dataset; ids and table slugs are exact. |
License
MIT. CVM data is public; see the CVM open-data portal for the original terms and update policy.
Metadata
Release files for cvm-mcp 0.1.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| cvm_mcp-0.1.1.tar.gz | 77.9 kB | With provenance | Details for cvm_mcp-0.1.1.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| cvm_mcp-0.1.1-py3-none-any.whl | Python 3 | none | any | Details for cvm_mcp-0.1.1-py3-none-any.whl |
Total release size: 160.7 kB
Release files / cvm_mcp-0.1.1.tar.gz
| Download URL | cvm_mcp-0.1.1.tar.gz |
|---|---|
| Size | 77.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
890526ec8c6a8db87659f61813c83f17130e5901f646c568f674c60a926b8482
|
|
BLAKE2b-256 checksum How to use checksums |
3d3a81f1d8d377be8dd39e2b491e57dc82cadca36a9e53bf4a792501faae4afb
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 6, 2026.
Transparency logRelease files / cvm_mcp-0.1.1-py3-none-any.whl
| Download URL | cvm_mcp-0.1.1-py3-none-any.whl |
|---|---|
| Size | 82.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
44111fad629ec545b36d4f57bf7dfc7530d081945da2a45c4daf1925ae8b2cff
|
|
BLAKE2b-256 checksum How to use checksums |
6b7f9e189f2d9394041001dd6b4ee5caf4e6bced8399416b41acf8e242beffbe
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 6, 2026.
Transparency logSecurity for cvm-mcp 0.1.1
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for cvm_mcp-0.1.1.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 6, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.