Skip to main content
Glama
MalindaBotheju

financial-analyst-mcp

README.md
# MCP-Powered Financial Analyst

A local-first AI financial analyst built around a real MCP (Model Context Protocol) server.
Instead of one chatbot guessing at stock trends, two agents work together:

- **Validator agent** — checks data pulled from the internet (Yahoo Finance, news APIs) for
  gaps, bad values, or ticker mismatches before anything reaches the LLM.
- **Analyst agent** — reasons over the validated data and MCP tool outputs to write a
  structured summary (trend, moving averages, volatility, and relevant news sentiment).

Both agents call tools exposed by a custom **MCP server** — this is the core of the project,
not just LangChain glue code.

## What I added beyond a basic tutorial version

- Two-agent split (Validator → Analyst) instead of one agent doing everything
- A real MCP server with 3 tools, including validation as its own tool
- LangSmith tracing on both agents
- Neon Postgres caching layer (avoids re-hitting rate-limited APIs)
- A small eval suite with hand-calculated ground truth
- A deployed web app (FastAPI on Render, static frontend on Vercel)

## Architecture

```
User query (ticker + date range)
        │
        ▼
  Validator agent ──calls──▶ MCP server ──▶ fetch_price_data (Yahoo Finance)
        │                                 ▶ validate_data
        │  (retries once on bad data)
        ▼
  Analyst agent ──calls──▶ MCP server ──▶ analyze_chart (moving averages, volatility)
        │                                ▶ get_news_sentiment
        ▼
  Structured summary + chart data → FastAPI → frontend
```

## Project layout

```
backend/
  mcp_server/          # the MCP server + its 3 tools (this is the centerpiece)
  agents/               # Validator agent, Analyst agent
  orchestration/         # LangGraph wiring: Validator -> Analyst
  db/                    # Neon Postgres cache + eval result storage
  evals/                 # hand-checked test cases + eval runner
  main.py                # FastAPI app, deployed on Render
frontend/
  index.html             # single-page UI, deployed on Vercel
  vercel.json
render.yaml               # Render deployment config for the backend
requirements.txt
.env.example
```

## Setup

1. **Clone and install**
   ```bash
   git clone <your-repo-url>
   cd financial-analyst-mcp
   python -m venv venv && source venv/bin/activate
   pip install -r requirements.txt
   ```

2. **Environment variables** — copy `.env.example` to `.env` and fill in:
   - `GROQ_API_KEY` — free, no payment required (sign up at console.groq.com), used by the Analyst agent and the news-relevance check
   - `DATABASE_URL` — your RDS Postgres connection string
   - `NEWSAPI_KEY` — free tier at newsapi.org (100 requests/day, no card required)
   - `LANGCHAIN_API_KEY`, `LANGCHAIN_PROJECT` — for LangSmith tracing (optional, free tier)

3. **Set up the database tables**
   ```bash
   python -m backend.db.database --init
   ```

4. **Run the backend locally**
   ```bash
   uvicorn backend.main:app --reload --port 8000
   ```

5. **Run the frontend locally** — just open `frontend/index.html` in a browser, or serve it:
   ```bash
   cd frontend && python -m http.server 5500
   ```
   Update the `API_BASE_URL` constant near the top of `index.html` to point at your backend
   (`http://localhost:8000` locally, your Render URL once deployed).

6. **Run the eval suite**
   ```bash
   python -m backend.evals.run_evals
   ```

## Deployment (AWS free tier)

Everything below stays within AWS's 12-month free tier (as long as you pick the free-tier
instance sizes noted). Nothing here needs code changes — the same FastAPI app and MCP server
run as-is.

### 1. Database → RDS (Postgres)
1. AWS Console → RDS → **Create database**.
2. Engine: PostgreSQL. Templates: **Free tier**.
3. Instance class: `db.t3.micro` (or `db.t4g.micro`, whichever the console offers as free tier).
4. Set a master username/password, note them.
5. Under "Connectivity," set **Public access: Yes** (needed so your EC2 instance and your own
   machine can reach it — for a portfolio project this is fine; tighten it later if you want).
6. Once it's up, copy the endpoint (looks like `xxxx.rds.amazonaws.com`) and build your
   connection string:
   ```
   DATABASE_URL=postgresql://<username>:<password>@<endpoint>:5432/postgres
   ```
7. In the RDS instance's **Security Group**, add an inbound rule: PostgreSQL (port 5432),
   source = your IP (for local testing) and later your EC2 instance's security group.

### 2. Backend → EC2
1. AWS Console → EC2 → **Launch instance**.
2. AMI: Amazon Linux 2023 (or Ubuntu 22.04). Instance type: **t2.micro** (free tier eligible).
3. Create/download a key pair (`.pem` file) — you need this to SSH in.
4. Security group: allow inbound SSH (port 22, your IP) and **custom TCP port 8000** from
   anywhere (`0.0.0.0/0`) so the frontend can reach the API.
5. Launch it, then SSH in:
   ```bash
   ssh -i your-key.pem ec2-user@<instance-public-ip>
   ```
6. On the instance:
   ```bash
   git clone <your-repo-url> financial-analyst-mcp
   cd financial-analyst-mcp
   nano .env          # paste in GROQ_API_KEY (free, from console.groq.com), DATABASE_URL (from RDS), NEWSAPI_KEY, etc.
   bash aws/ec2-setup.sh
   ```
   This installs Python, dependencies, creates the database tables, and starts the backend as a
   `systemd` service (`financial-analyst`) that keeps running even after you disconnect and
   restarts automatically if the instance reboots.
7. Confirm it's up: `curl http://<instance-public-ip>:8000/health`

### 3. Frontend → S3 static website
1. Update `API_BASE_URL` near the top of `frontend/index.html` to
   `http://<your-ec2-public-ip>:8000`.
2. From your own machine (with the [AWS CLI](https://aws.amazon.com/cli/) installed and
   `aws configure` run once with your credentials):
   ```bash
   BUCKET_NAME=your-unique-bucket-name bash aws/deploy-frontend-s3.sh
   ```
3. The script creates the bucket, turns on static website hosting, and uploads `index.html`.
   It prints your live URL at the end (`http://<bucket>.s3-website-<region>.amazonaws.com`).
4. Whenever you change the frontend, just re-run the same script to re-upload.

### Notes
- EC2's public IP changes if you stop/start the instance (unless you attach an Elastic IP,
  which is also free as long as it's attached to a running instance). If your IP changes,
  update `API_BASE_URL` and re-run the S3 deploy script.
- To keep costs at zero, stick to the `t2.micro`/`db.t3.micro` free-tier sizes and remember AWS
  free tier covers your first 12 months only.

### Will this cost anything on AWS free tier?

**No, as long as you stay within these limits (all part of AWS's 12-month free tier for new accounts):**

| Service | Free tier limit | This project's usage |
|---|---|---|
| EC2 (t2.micro/t3.micro) | 750 hours/month | 1 instance running 24/7 = ~730 hrs — fits inside the limit |
| RDS (db.t3.micro/t4g.micro) | 750 hours/month + 20GB storage | 1 database instance = fits inside the limit |
| S3 | 5GB storage, 20,000 GET / 2,000 PUT requests/month | A single HTML file and light portfolio traffic — nowhere close |
| Data transfer out | 100GB/month (first 12 months) | Portfolio-level traffic won't get near this |

**Things that WILL cost money, so avoid them:**
- Launching a **second** EC2 instance or RDS database at the same time (only your first free-tier
  hours per service are free — running two eats double the hours)
- Using a larger instance type than `t2.micro`/`t3.micro` (EC2) or `db.t3.micro`/`db.t4g.micro` (RDS)
- Requesting an **Elastic IP** and then *not* attaching it to a running instance — AWS charges for
  unattached Elastic IPs specifically to discourage this
- If your AWS account isn't brand new — free tier is 12 months from account creation, so if you've
  had the account longer than that, these services will bill normally

**One safety net worth setting up regardless:** AWS Console → Billing → Budgets → create a budget
alert for $1. It emails you the moment anything starts costing money, so you're never surprised.

## Notes on the MCP layer

The MCP server (`backend/mcp_server/server.py`) is a standalone process that speaks the Model
Context Protocol over stdio. The agents connect to it as MCP clients using
`langchain-mcp-adapters`, which converts the MCP tools into LangChain-compatible tools
automatically. This means the same MCP server could be plugged into Claude Desktop, Cursor, or
any other MCP-compatible host with zero changes — that's the whole point of building it this
way instead of hardcoding tool schemas into the agent.