One SQL tool for Brazilian market data.

better-investment-mcp is an open-source MCP server that gives your AI agent Brazilian prices, dividends, fundamentals, filings and macro series. It fills a local DuckDB cache on demand, and the agent answers with one read‑only SELECT.

Star on GitHub

npx -y better-investment-mcp init

query_sql · read-only illustrative
agentrefused
SELECT reference_time_start, close
FROM   asset_quotes
WHERE  code = 'PETR4'
ORDER BY reference_time_start DESC LIMIT 3
Refused. asset_quotes needs a filter on asset_id (market:asset_type:code). Add asset_id = 'BR:stock:PETR4'.
agent, againaccepted
SELECT reference_time_start, close
FROM   asset_quotes
WHERE  asset_id = 'BR:stock:PETR4'
ORDER BY reference_time_start DESC LIMIT 3
loadedBR:stock:PETR4 · quotes
reference_time_startclose
2026-11-06example
2026-11-05example
2026-11-04example

Every query names its asset. No exceptions.

The server reads the filter, loads what is missing or expired, then runs the query. A query without it is refused, and the answer says which filter to add.

BR:fii:HGLG11 BR:fund:00.017.024/0001-53 BR:treasury:SELIC_2029-03-01 BR:index:IPCA BR:tax_rate:CDI BR:currency:USD
  • Literal filters onlyCompared with = or IN against literal text, up to 50 assets per query.
  • asset_metrics also needs a metricA metric is group.name, like fii.net_worth or macro.value. The unit comes on every row.
  • Joins work, filters do not travelEach asset table in a join needs its own asset_id filter. The guard reads the query with DuckDB's own parser.

Six kinds of data, one way to read them.

Open data first. Each load records which vendor produced it, so a number can be traced back.

01

Prices and dividends

Yahoo Finance, yfinance, B3 COTAHIST

Daily quotes, dividends and splits. Stale days and suspect ticks are flagged, not dropped.

asset_quotes
02

Company statements

CVM ITR and DFP

Quarterly and yearly lines with period and version, consolidated and individual.

statement.*
03

FIIs and funds

CVM monthly and daily open files

Net worth and balance columns per month, fund quota, holders and holdings by CNPJ.

fii.* · fund.*
04

Filings as searchable text

CVM IPE and FRE, FNET

Reports are cut by section and page, so search_chunks finds the passage and where it lives.

asset_filing_chunks
05

Macro, rates and expectations

BCB SGS, Focus, PTAX

IPCA, IGP-M, CDI, SELIC, closing exchange rates and the annual Focus survey.

macro.* · fx.*
06

Tesouro Direto

Tesouro Transparente

Buy and sell rates and unit prices per bond, daily history from 2004.

treasury.*

The cache fills itself. The answer says how.

Every answer reports what was loaded and what failed. If a load runs long, the query returns the rows already stored and the load keeps going in the background.

1

filter

The guard reads asset, metric and period from the SQL.

2

load

Missing or expired data comes from a vendor into DuckDB.

3

read

One SELECT on the cache. Nothing else can run.

4

answer

Rows, a loaded report, and the passages found.

First query for an asset
Next query, cache fresh
Not to scale. A load waits 5 seconds by default (INVESTMENT_MCP_BACKGROUND_AFTER_SECONDS), then answers with what is stored plus a load id.

Ask in plain words. The agent writes the SQL.

What was the dividend yield of PETR4 over the last 12 months?
readsasset_quotes
asset_dividends
Which FIIs in my list had the largest change in net worth last quarter?
readsasset_metrics
fii.net_worth
Find the passage about vacancy in the latest management report of HGLG11.
readsasset_filing_chunks
search_chunks()

Four tools. Three only read.

Nothing in the server changes anything outside its own cache file, except one notice: `notify_load_finish` sends a POST to an https address you give when a background load finishes (ADR 0028). That rule lives in code with a test, not in a prompt.

get_overview

The server's usage notice, the tables, and what is loaded now. Call it first.

get_guide

A short guide for one topic of query_sql: sql, search, sources or limits.

query_sql

One SELECT over the data tables, with the filter rule enforced by the SQL guard.

tools/list → 3 toolsno write tool exists

One command, then ask.

No account or key is needed for the open data sources. You only need an MCP client such as Claude, Cursor or Codex.

Add this to the MCP servers of your client.

{
  "mcpServers": {
    "better-investment": {
      "command": "npx",
      "args": ["-y", "better-investment-mcp"]
    }
  }
}
SettingDefault
INVESTMENT_MCP_CACHE_DIRsystem temp dir
INVESTMENT_MCP_CACHE_TTL_SECONDS86400
INVESTMENT_MCP_BACKGROUND_AFTER_SECONDS5
INVESTMENT_MCP_TRANSPORTstdio
INVESTMENT_MCP_HTTP_PORT3100
INVESTMENT_MCP_QUOTES_PROVIDERBR:yfinance>b3

Point the cache at a folder of your own to keep it between runs. Deleting the file loses nothing: the next query loads the data again. Print your client config with npx -y better-investment-mcp init.

Private by construction. Data, never instructions.

Writes only to its own cache

Each vendor in the sources list is the only place it fetches from. Nothing is sent anywhere else.

Token on every request

In HTTP mode, /mcp needs the bearer token on every address. It is never returned or logged.

Filings are data

Text from reports and other third-party documents is data, never an instruction to your agent.

Not investment advice. The output describes and locates information. It never recommends what to buy or sell. This is an unofficial project, not affiliated with B3, CVM, Yahoo or any other data vendor.

Questions worth asking first.

Does it tell me what to buy?
No. It describes and locates information. It is not investment advice, a recommendation or an analysis of what to buy or sell.
Is it ready for daily use?
It is a work in progress. The server runs and its tools answer, but the data behind them arrives issue by issue. The sources list and the repository show what exists today.
Why does a query say a source is blocked?
A site that refuses this server, like StatusInvest answering with a Cloudflare challenge, is reported in the loaded report with its error. The server does not bypass it. Other providers still answer, and a fallback is used where one is set.
Can I delete the cache?
Yes. It is one DuckDB file and it is disposable. The next query loads the data again. One cache file serves one running server at a time.
Which AI clients work?
Any client that speaks MCP, such as Claude, Cursor and Codex. Run init to print the configuration for each.

Read the data. Keep the judgment yours.

npx -y better-investment-mcp init
Star on GitHub