Skip to main content
Glama
PiyushDubey007

Financial Context Engine

README.md
# Financial Context Engine

An MCP (Model Context Protocol) server that exposes personal financial data — transaction ledger, portfolio holdings, live/historical market prices, and quantitative risk metrics — as standardized Tools, Resources, and Prompts. Any MCP host (Claude Desktop, Claude.ai, Cursor) can connect to it and reason over **real, deterministically computed numbers** instead of estimating them.

**This project is 100% pure Python. It runs natively on Windows — no WSL, no Docker, no Linux subsystem required.** DuckDB, FastMCP, numpy/pandas, and yfinance are all pip-installable packages with native Windows wheels.

---

## Architecture

```
┌──────────────────────────────────────────────┐
│         MCP HOST (Claude Desktop, etc.)       │
└────────────────────┬───────────────────────────┘
                     │ MCP protocol (stdio)
┌────────────────────▼───────────────────────────┐
│        FINANCIAL CONTEXT ENGINE (this repo)     │
│  ┌───────────┐ ┌────────────┐ ┌──────────────┐  │
│  │ RESOURCES │ │   TOOLS    │ │   PROMPTS    │  │
│  └─────┬─────┘ └─────┬──────┘ └──────┬───────┘  │
└────────┼─────────────┼───────────────┼──────────┘
        │             │               │
┌────────▼─────────────▼───────────────▼──────────┐
│   DuckDB   │  Rate Limiter + yfinance  │ Quant   │
│  (local)   │  (token bucket, cached)   │ (numpy) │
└───────────────────────────────────────────────────┘
```

No LLM lives inside this server. It's a data + compute backend that any MCP-compatible chat client connects to.

---

## Setup on Windows (VS Code, no WSL)

### 1. Install Python
Python 3.11+ from [python.org](https://www.python.org/downloads/) (check "Add to PATH" during install). Verify in a **regular Windows PowerShell or VS Code terminal** (not WSL):
```powershell
python --version
```

### 2. Open the project folder in VS Code
`File > Open Folder...` → select the `financial-context-engine` folder.

### 3. Create a virtual environment
In the VS Code terminal (make sure it's PowerShell or Command Prompt, not a WSL terminal — check the terminal dropdown in the top-right of the terminal panel):
```powershell
python -m venv .venv
.venv\Scripts\activate
```
Your prompt should now show `(.venv)`. If PowerShell blocks the activation script with an execution-policy error, run this once (you've hit this before with other projects):
```powershell
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
```

### 4. Install dependencies
```powershell
pip install -r requirements.txt
```

### 5. Copy the environment template
```powershell
copy .env.example .env
```
Defaults work out of the box (in-memory rate limiting, no Redis needed). Only edit `.env` if you want an Alpha Vantage API key or Redis-backed rate limiting later.

### 6. Seed demo data
```powershell
python -m db.seed
```
This creates `data/finance.duckdb` with ~6 months of synthetic transactions and 7 sample holdings — safe to demo, no real financial data involved.

### 7. Run the full test suite
```powershell
pytest tests/ -v
```
You should see **33 passed**.

### 8. Run the server in MCP Inspector (interactive dev mode)
```powershell
fastmcp dev server.py
```
This opens a browser-based inspector where you can call every tool and view every resource manually — no LLM needed, no config files needed. This is the fastest way to confirm everything works after any change.

---

## Connecting to Claude Desktop

Once you've verified the server works in MCP Inspector, add it to Claude Desktop's config so you can chat with it directly.

1. Open Claude Desktop → Settings → Developer → Edit Config (or find `claude_desktop_config.json` directly — on Windows it's usually at `%APPDATA%\Claude\claude_desktop_config.json`).
2. Add an entry (use the **full absolute Windows path** to your project and to the venv's python.exe):

```json
{
  "mcpServers": {
    "financial-context-engine": {
      "command": "C:\\path\\to\\financial-context-engine\\.venv\\Scripts\\python.exe",
      "args": ["C:\\path\\to\\financial-context-engine\\server.py"]
    }
  }
}
```

3. Restart Claude Desktop. You should see a 🔨 tools icon indicating the server connected — try asking: *"What's my current net worth?"* or *"Compute my portfolio's Sharpe ratio."*

---

## Project Structure

```
financial-context-engine/
├── server.py                  # MCP server entrypoint — run this
├── db/
│   ├── schema.sql              # DuckDB table definitions
│   ├── models.py                # Pydantic models (typed, shared everywhere)
│   ├── store.py                  # LedgerStore — the only module that touches SQL
│   └── seed.py                    # generates synthetic demo data
├── market/
│   ├── rate_limiter.py         # token bucket (in-memory + Redis modes)
│   ├── client.py                 # yfinance wrapper: cache -> rate limiter -> fetch
│   ├── history.py                  # historical price fetching for quant calcs
│   └── demo.py                       # standalone script proving the limiter works
├── analytics/
│   └── quant.py                 # Sharpe, Monte Carlo VaR, correlation, HHI — pure numpy
├── mcpapp/                    # (named mcpapp, not mcp, to avoid shadowing the MCP SDK)
│   ├── context.py               # wires store + rate limiter + market client together
│   ├── tools.py                  # @mcp.tool() definitions
│   ├── resources.py                # @mcp.resource() definitions
│   ├── prompts.py                    # @mcp.prompt() definitions
│   └── stream.py                       # background polling for "live" prices
└── tests/                     # 33 tests across every module above
```

---

## What Each Tool Does

| Tool | Purpose |
|---|---|
| `get_market_quote(symbols)` | Current price/P-E/52-week range, rate-limited + cached |
| `query_ledger(category, start_date, min_amount)` | Filter transaction history |
| `get_recent_transactions(days)` | Last N days of transactions |
| `get_net_worth()` | Cash + invested totals, per-account breakdown |
| `compute_portfolio_health_report()` | Sharpe ratio, Monte Carlo VaR (95%/99%, 1-day/1-month), correlation matrix, concentration (HHI) — **all computed in Python, not estimated by the model** |
| `get_live_prices()` | Latest background-polled prices for your holdings |
| `get_rebalance_suggestions(target_allocation)` | Dollar-value buy/sell suggestions vs. a target allocation |

## What Each Resource Exposes

| Resource URI | Content |
|---|---|
| `finance://summary` | Net worth snapshot |
| `finance://transactions/recent` | Last 30 days of transactions |
| `finance://portfolio/holdings` | Current holdings |
| `finance://portfolio/live` | Background-polled live prices |
| `finance://api-usage` | Rate limiter decision log (allowed/blocked/cached) — transparency into throttling |

---

## Design Notes

**Why deterministic Python math instead of letting the LLM calculate?** LLMs are unreliable at multi-step arithmetic, especially statistics like Sharpe ratio or Value-at-Risk where a small error compounds. Every number this server returns is computed by numpy/pandas from real data, so the model's job is reasoning and communication, not arithmetic.

**Why rate-limit market data calls?** Free-tier providers (yfinance's soft limits, Alpha Vantage's 25 calls/day) get exhausted fast if an LLM calls them on every message. The token-bucket limiter (reused from a standalone Distributed Rate Limiter project) plus a 60-second TTL cache keeps usage sane — see `market/demo.py` for a runnable proof: 50 rapid requests → 5 fetched, 30 cache hits, 15 correctly throttled.

**Why polling instead of true SSE push for "live" prices?** True MCP resource-subscription push depends on transport/session details that vary across FastMCP versions and host clients, and is genuinely fragile to get right without a Linux dev environment. A background-thread poller (`mcpapp/stream.py`) is the reliable choice here — it reads from the same rate-limited, cached client, so it never floods the market data provider even while "live." If you later deploy this on Linux/Codespaces, the polling loop can be swapped for `mcp.send_resource_updated()` calls without touching the state management.

**Security & privacy:**
- All credentials load from `.env` (gitignored), never logged, never returned in tool output.
- `data/finance.duckdb` is local-only and gitignored — nothing syncs to the cloud.
- The only outbound network calls are to the market data provider (ticker symbol only, no personal data).
- Redis mode (for shared rate-limiting across multiple server instances) is opt-in via `.env`, not required for normal use.

---

## Running Individual Test Files

```powershell
pytest tests/test_store.py -v          # data layer (6 tests)
pytest tests/test_rate_limiter.py -v    # token bucket (6 tests)
pytest tests/test_market_client.py -v    # market data + cache (5 tests)
pytest tests/test_quant.py -v              # Sharpe/VaR/correlation (10 tests)
pytest tests/test_stream.py -v               # live price polling (3 tests)
pytest tests/test_tools.py -v                  # MCP tool logic (3 tests)
python -m tests.smoke_test                       # end-to-end, no pytest needed
python -m market.demo                              # rate limiter demo output
```

Maintenance

ActivityMaintained
ResponsivenessNo issues