Skip to main content
Glama

Financial Research Copilot (FINCO)

A retrieval-augmented research assistant over SEC EDGAR filings, exposed as an MCP server so it can be plugged directly into Claude Desktop or any other MCP-compatible client, plus a standalone Streamlit terminal for demoing it without an LLM client.

Ask an LLM things like "What are Apple's biggest risk factors this quarter?" or "Give me a snapshot of NVDA — price, fundamentals, and the relevant 10-K excerpt" and it answers by pulling live market data and searching a locally-indexed corpus of filings, instead of hallucinating.

FINCO terminal — live IBM quote, fundamentals, and a reranked 10-K excerpt

The Streamlit terminal answering a live query: quote + fundamentals from Alpha Vantage's demo key, and the excerpt retrieved from IBM's latest 10-K by the hybrid + reranked pipeline. Macro panel needs a (free) FRED key.

Why this exists

Most RAG demos stop at "chunk a PDF, embed it, ask an LLM." This project is closer to something you'd actually run in production for multiple customers:

  • Hybrid retrieval, not just vector search — BM25 (lexical) and dense embeddings run in parallel and are combined with Reciprocal Rank Fusion, then re-ranked with a cross-encoder. Pure semantic search misses exact ticker/term matches; pure keyword search misses paraphrases. On the committed eval set the full pipeline beats dense-only by +75% recall@5 and +39% MRR (numbers below).

  • A retrieval eval harness, not vibes — src/retrieval/eval runs a fixed query set against any retrieval version and logs recall@k/precision@k/MRR/latency to Postgres, so changes to the pipeline (chunking, fusion weights, reranker) can be compared against a baseline instead of eyeballed. It earns its keep: it caught that plainto_tsquery ANDs every term, which silently zeroed out BM25 for natural-language questions — fusion looked useless until the eval showed why.

  • Multi-tenant data isolation via Postgres Row-Level Security, not an app-layer WHERE tenant_id = ... that's one missed clause away from a data leak (db/rls_policies.sql). The database enforces isolation even if application code forgets.

  • Delta-aware ingestion — filings are hashed at the section level so a 10-K that hasn't materially changed since the last crawl isn't re-chunked and re-embedded (src/ingestion/delta.py), which matters once you're tracking a real watchlist daily.

Related MCP server: northbridge-diligence

Architecture

flowchart TD
    EDGAR[SEC EDGAR] --> Crawler[crawler] --> Delta[delta detection] --> Chunker[chunker] --> Embedder[embedder] --> PG[(Postgres + pgvector)]
    PG --> BM25[BM25 search]
    PG --> Dense[dense vector search]
    BM25 --> RRF[Reciprocal Rank Fusion]
    Dense --> RRF
    RRF --> Rerank[cross-encoder rerank]
    Rerank --> MCP[MCP server tools<br/>Claude Desktop, etc.]
    Rerank --> UI[Streamlit terminal<br/>public demo UI]

Live market data (quote, fundamentals, macro indicators) is fetched from Alpha Vantage / FRED alongside the filing search, cached per-tool with a configurable TTL, and merged into a single "company snapshot" response.

A scheduled poller (src/streaming/poller.py) also watches the filing feed and can dispatch webhook/email alerts when a followed ticker files a new 10-K/10-Q/8-K.

Retrieval quality

Measured on a corpus of 24 real filings (10-K/10-Q/8-K for AAPL, MSFT, NVDA, IBM; ~730 chunks) with 12 labeled queries. Ground truth per query is defined by ranker-independent SQL predicates (ticker + form type + section

  • keyword conjunctions), so no retriever is favored by construction.

Version

recall@5

precision@5

recall@10

MRR

latency*

v1 dense-only

0.164

0.217

0.239

0.475

297 ms

v2 hybrid (BM25 + dense + RRF)

0.184

0.200

0.259

0.488

97 ms

v3 hybrid + cross-encoder rerank

0.288

0.333

0.288

0.663

313 ms

* Cold-start skew: v1 runs first and its average includes one-time embedding-model load. All models run locally on CPU.

Reproduce with python -m src.retrieval.eval.run_all, which rewrites eval_baseline.json — the committed baseline that tests/test_eval_regression.py gates against in CI (fails if recall@5 drops more than 2 points).

Delta-aware ingestion, measured on a second daily run over the same 24 filings: sections_skipped=28 sections_reembedded=0 skip_ratio=100% — nothing is re-chunked or re-embedded unless a section's content hash actually changed.

Stack

Layer

Choice

Language

Python 3.10+

Database

PostgreSQL 16 + pgvector (HNSW index)

Dense embeddings

sentence-transformers (all-MiniLM-L6-v2, local — no API key)

Lexical search

Postgres tsvector/GIN, fused via rank-bm25

Reranking

local cross-encoder (ms-marco-MiniLM-L-6-v2)

Serving

MCP server (mcp[cli], stdio transport)

Demo UI

Streamlit

Ingestion

httpx + beautifulsoup4/lxml against SEC EDGAR

CI

GitHub Actions (ruff + pytest), plus a scheduled daily-ingest workflow

MCP tools

Tool

Description

get_quote

Live stock price (Alpha Vantage, cached)

get_fundamentals

Key financial metrics (Alpha Vantage, cached)

get_macro_indicator

Macro series from FRED — treasury yields, CPI, Fed funds rate, unemployment

get_filing_excerpt

Hybrid RAG search over ingested SEC filings for a ticker + topic

get_company_snapshot

Merges the above into one response: quote + fundamentals + macro context + relevant filing excerpt

Running it

Prerequisites: Docker (for Postgres/pgvector), Python 3.10+.

# 1. Start the database
docker compose up -d

# 2. Install the project (editable, with dev deps)
pip install -e ".[dev]"

# 3. Configure environment
cp .env.example .env
# fill in SEC_EDGAR_USER_AGENT (required by SEC's fair-use policy),
# and optionally ALPHA_VANTAGE_API_KEY / FRED_API_KEY for live market data

# 4. Load the schema
psql "$DATABASE_URL" -f db/schema.sql
psql "$DATABASE_URL" -f db/rls_policies.sql

# 5. Ingest some filings for a watchlist ticker
python -m src.ingestion.run_daily

# (optional) score all three retrieval versions against the eval set
python -m src.retrieval.eval.run_all

# 6a. Run the MCP server (point Claude Desktop / an MCP client at this)
python -m src.mcp_server.server

# 6b. ...or run the standalone demo UI instead
streamlit run public_app/streamlit_app.py

Tests

pytest tests/ -m "not integration"   # unit tests, no external calls
pytest tests/                        # includes tests that hit real APIs

Repo layout

src/
  ingestion/     SEC EDGAR crawler, section-level delta detection, chunking, embedding
  retrieval/     BM25 + dense search, RRF fusion, cross-encoder reranking, eval harness
  mcp_server/    MCP tool definitions + server entrypoint
  streaming/     Filing poller + alert dispatch
db/              Schema + row-level security policies
public_app/      Streamlit demo UI
tests/           Unit + integration tests

Status

This is a personal/portfolio project, not a production service. It's not deployed anywhere persistent; the daily-ingest GitHub Action and the Streamlit app are meant to be run against your own Postgres instance. API keys for Alpha Vantage and FRED are free-tier and not included.

License

MIT — see LICENSE.

Maintenance

ActivitySlowing
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    B
    maintenance
    MCP server for analyzing SEC filings (10-K, 10-Q, 8-K) with industry-aware financial extraction and BERT-based NLP.
    1
    MIT
  • A
    license
    A
    quality
    B
    maintenance
    An MCP server that wraps SEC EDGAR APIs to provide company financial data, screening metrics, and disclosure signals for investment diligence, with every figure traced to its source filing.
    8
    MIT
  • A
    license
    A
    quality
    A
    maintenance
    An MCP server that enables models to access SEC EDGAR filings, filing text, and XBRL financial facts with caching, rate limiting, and iXBRL stripping.
    8
    MIT