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.
npx -y better-investment-mcp init
SELECT reference_time_start, close FROM asset_quotes WHERE code = 'PETR4' ORDER BY reference_time_start DESC LIMIT 3
asset_quotes needs a filter on asset_id (market:asset_type:code). Add asset_id = 'BR:stock:PETR4'.SELECT reference_time_start, close FROM asset_quotes WHERE asset_id = 'BR:stock:PETR4' ORDER BY reference_time_start DESC LIMIT 3
| reference_time_start | close |
|---|---|
| 2026-11-06 | example |
| 2026-11-05 | example |
| 2026-11-04 | example |
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.
- Literal filters onlyCompared with
=orINagainst literal text, up to 50 assets per query. asset_metricsalso needs a metricA metric isgroup.name, likefii.net_worthormacro.value. The unit comes on every row.- Joins work, filters do not travelEach asset table in a join needs its own
asset_idfilter. 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.
Prices and dividends
Yahoo Finance, yfinance, B3 COTAHISTDaily quotes, dividends and splits. Stale days and suspect ticks are flagged, not dropped.
asset_quotesCompany statements
CVM ITR and DFPQuarterly and yearly lines with period and version, consolidated and individual.
statement.*FIIs and funds
CVM monthly and daily open filesNet worth and balance columns per month, fund quota, holders and holdings by CNPJ.
fii.* · fund.*Filings as searchable text
CVM IPE and FRE, FNETReports are cut by section and page, so search_chunks finds the passage and where it lives.
Macro, rates and expectations
BCB SGS, Focus, PTAXIPCA, IGP-M, CDI, SELIC, closing exchange rates and the annual Focus survey.
macro.* · fx.*Tesouro Direto
Tesouro TransparenteBuy 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.
filter
The guard reads asset, metric and period from the SQL.
load
Missing or expired data comes from a vendor into DuckDB.
read
One SELECT on the cache. Nothing else can run.
answer
Rows, a loaded report, and the passages found.
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?
asset_dividends
Which FIIs in my list had the largest change in net worth last quarter?
fii.net_worth
Find the passage about vacancy in the latest management report of HGLG11.
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_overviewThe server's usage notice, the tables, and what is loaded now. Call it first.
get_guideA short guide for one topic of query_sql: sql, search, sources or limits.
query_sqlOne SELECT over the data tables, with the filter rule enforced by the SQL guard.
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"]
}
}
}The client starts the container. The cache lives in a named volume.
docker run -i --rm \ -v better-investment-cache:/cache \ ghcr.io/guilhermebkel/better-investment-mcp
Runs on your machine. Every request must send a bearer token of at least 8 characters.
docker run -d --name investment-mcp \ -p 127.0.0.1:3100:3100 \ -e INVESTMENT_MCP_TRANSPORT=http \ -e INVESTMENT_MCP_HTTP_HOST=0.0.0.0 \ -e INVESTMENT_MCP_HTTP_TOKEN='your-long-random-secret' \ -v better-investment-cache:/cache \ ghcr.io/guilhermebkel/better-investment-mcp
Then point the client at http://127.0.0.1:3100/mcp with an Authorization: Bearer header. Put a TLS proxy in front before exposing it beyond localhost.
| Setting | Default |
|---|---|
INVESTMENT_MCP_CACHE_DIR | system temp dir |
INVESTMENT_MCP_CACHE_TTL_SECONDS | 86400 |
INVESTMENT_MCP_BACKGROUND_AFTER_SECONDS | 5 |
INVESTMENT_MCP_TRANSPORT | stdio |
INVESTMENT_MCP_HTTP_PORT | 3100 |
INVESTMENT_MCP_QUOTES_PROVIDER | BR: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?
Is it ready for daily use?
Why does a query say a source is blocked?
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?
Which AI clients work?
init to print the configuration for each.Read the data. Keep the judgment yours.
npx -y better-investment-mcp init