Follow-up cleanup deferred from #29 (curated MCP surface). Cross-file, source-layer changes kept out of #29 to keep that PR contained. Origin: code review of #29.
1. Push limit into the source functions
The API/MCP layers fetch the full dataset then slice (await fn(...))[:limit] in Python, so the duplicated truncation lives at every call site. Sources called this way: companies.get_companies, financials.get_dfp/get_itr, funds.get_fund_catalog/get_fund_daily, lamina.get_fund_lamina/get_fund_monthly_returns/get_fund_yearly_returns, profile.get_fund_profile, fip.get_fip, arrecadacao.get_arrecadacao, empresas.get_susep_empresas, leiloes.get_aneel_leiloes_geracao/_transmissao, anbima.get_ima/get_debentures. bonds.get_treasury_bonds already takes limit and is the model. Add an optional limit: int | None = None (default None preserves current behavior) so truncation happens once, at the source.
2. Dedup the B3 quotes guard
mcp_app._b3_quotes() duplicates routers/b3.py:_quotes() (same lazy yfinance import plus 503). The b3_quote handler body (ticker split, max-20 check, single-vs-multi dispatch) also clones the REST one. Extract a shared resolver and dispatch into findata.sources.b3 and call it from both.
3. Push the anbima emissor filter into the source
mcp_app.anbima_tool filters debentures by emissor substring inside the handler. Every other source-level filter lives in the source module so REST/CLI/MCP share it. Move emissor into anbima.indices.get_debentures.
Follow-up cleanup deferred from #29 (curated MCP surface). Cross-file, source-layer changes kept out of #29 to keep that PR contained. Origin: code review of #29.
1. Push
limitinto the source functionsThe API/MCP layers fetch the full dataset then slice
(await fn(...))[:limit]in Python, so the duplicated truncation lives at every call site. Sources called this way:companies.get_companies,financials.get_dfp/get_itr,funds.get_fund_catalog/get_fund_daily,lamina.get_fund_lamina/get_fund_monthly_returns/get_fund_yearly_returns,profile.get_fund_profile,fip.get_fip,arrecadacao.get_arrecadacao,empresas.get_susep_empresas,leiloes.get_aneel_leiloes_geracao/_transmissao,anbima.get_ima/get_debentures.bonds.get_treasury_bondsalready takeslimitand is the model. Add an optionallimit: int | None = None(default None preserves current behavior) so truncation happens once, at the source.2. Dedup the B3 quotes guard
mcp_app._b3_quotes()duplicatesrouters/b3.py:_quotes()(same lazy yfinance import plus 503). Theb3_quotehandler body (ticker split, max-20 check, single-vs-multi dispatch) also clones the REST one. Extract a shared resolver and dispatch intofindata.sources.b3and call it from both.3. Push the anbima
emissorfilter into the sourcemcp_app.anbima_toolfilters debentures byemissorsubstring inside the handler. Every other source-level filter lives in the source module so REST/CLI/MCP share it. Moveemissorintoanbima.indices.get_debentures.