Search is the easy part; the work is upstream — download the filing, OCR it into Markdown, embed it into Chroma. Nothing comes back until that has happened for the ticker and year you are asking about. In exchange the retrieval is genuinely hybrid rather than vector-only: BM25 and dense results fused, then reranked.
The MCP surface of a Python toolkit that downloads SEC filings, converts them to Markdown with olmOCR, chunks and indexes them in Chroma alongside earnings transcripts, and searches the result. The MCP server exposes the search half; ingestion runs through the CLI and a FastAPI app.
- Hybrid search over indexed SEC filings — dense retrieval and BM25 fused by reciprocal rank, then reranked — `search_sec_filings_tool`
- The same pipeline over earnings-call transcripts — `search_transcripts_tool`
- Queries scoped by ticker, year and filing type, so a question lands inside one 10-K rather than the whole index
This is a stack, not a one-line install. `uv sync --group ocr-md --group mcp`, then start the model servers with `make vllm-olmocr-serve`, `make vllm-embd-serve` and `make vllm-reranker-serve` before running `python mcp_server.py`. Configuration comes from `.env`: `SEC_API_ORGANIZATION` and `SEC_API_EMAIL` for SEC access, `OLMOCR_SERVER`, `OLMOCR_MODEL` and `OLMOCR_WORKSPACE` for OCR, `EMBEDDING_SERVER` and `EMBEDDING_MODEL` for embeddings, `CHROMA_PERSIST_DIR` for the vector store, and `MCP_HOST`, `MCP_PORT` and `MCP_NGROK_ALLOWED_HOSTS` for the network side. Filings must be downloaded and embedded before either tool returns anything.
One command — uvx finance_data_llm
