StockAnalysis
# š MCP Stock Analysis Server
A full-featured **Model Context Protocol (MCP)** server for real-time stock analysis, built as a reference implementation demonstrating how to combine **MCP tools**, **live API data fetching**, **JSON file persistence**, and **rich interactive Prefab UI dashboards** ā all wired together in a clean Python codebase.
---
## Intention ā Why This Project Exists
This project serves as a **comprehensive MCP example** that goes beyond "hello world". It demonstrates:
| Concept | What This Project Shows |
|---------|------------------------|
| **MCP Tool Registration** | 7 tools registered via `@mcp.tool()` decorators on a `MCPServer` instance |
| **Live API Integration** | Fetching real-time financial data from Yahoo Finance via `yfinance` |
| **Data Persistence** | Every API response is written to disk as structured JSON with timestamps |
| **Rich UI via Prefab** | Each tool returns a fully interactive HTML dashboard (charts, gauges, tables) built with `prefab-ui` |
| **HTML Export** | Dashboards are also saved as standalone `dashboard.html` for browser viewing |
| **MCP Client** | A standalone `test_client.py` that connects over **stdio transport**, lists tools, and calls them interactively |
| **Claude Desktop Skill** | Ready-to-paste configuration for plugging this server into Claude Desktop or any MCP host |
| **Multi-Market Support** | Handles US (NYSE/NASDAQ), Indian NSE (`.NS`), and BSE (`.BO`) tickers |
Whether you're learning MCP, building your own tools, or want a working stock analysis agent ā this project is a complete starting point.
---
## Architecture Overview
```
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā MCP Client ā
ā (Claude Desktop / test_client.py / Any MCP Host) ā
ā ā
ā Connects via stdio transport āāāŗ ClientSession āāāŗ call_tool() ā
āāāāāāāāāāāāāāāāāāāāāāāāāā¬āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā stdio (stdin/stdout)
ā¼
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā MCP Server (server.py) ā
ā ā
ā MCPServer("StockAnalysis") ā
ā āāā @mcp.tool() analyze_stock(ticker) ā
ā āāā @mcp.tool() get_stock_price(ticker) ā
ā āāā @mcp.tool() get_stock_history(ticker, period) ā
ā āāā @mcp.tool() get_financials(ticker) ā
ā āāā @mcp.tool() compare_peers(ticker) ā
ā āāā @mcp.tool() get_technical_analysis(ticker) ā
ā āāā @mcp.tool() get_sector_overview(sector) ā
ā ā
ā Each tool: ā
ā 1. Calls stock_data.py āāŗ yfinance API ā
ā 2. Calls file_writer.py āāŗ data/*.json ā
ā 3. Calls ui/dashboard.py āāŗ PrefabApp (interactive HTML) ā
ā 4. Returns serialized JSON to the client ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
```
---
## Server Details
### Entry Point & Transport
The server is defined in [`server.py`](src/server.py) and uses the **`MCPServer`** class from the `mcp` Python SDK (FastMCP):
```python
from mcp.server.mcpserver import MCPServer
mcp = MCPServer(
"StockAnalysis",
instructions="A stock analysis server that provides comprehensive financial data..."
)
```
- **Transport**: stdio (stdin/stdout) ā the standard transport for AI assistants like Claude
- **Logging**: All logs go to **stderr** (required for stdio transport so logs don't interfere with MCP protocol messages)
- **Entry point**: `src.server:main` (registered in `pyproject.toml` as the `stock-mcp` console script)
### Server Startup
```python
def main():
"""Entry point for the MCP server."""
mcp.run()
```
When `mcp.run()` is called, the server:
1. Starts listening on stdin for MCP protocol messages
2. Responds to `initialize`, `list_tools`, and `call_tool` requests
3. Returns tool results (text or Prefab UI JSON) via stdout
---
## Available Tools (Functions)
The server exposes **7 tools**, each decorated with `@mcp.tool()`. Tools accept simple typed parameters and return either plain text or serialized Prefab UI JSON.
### 1. `analyze_stock(ticker: str) ā str`
**Full comprehensive analysis** ā the flagship tool. Fetches everything and builds a multi-tab dashboard using Prefab.
| Step | Action |
|------|--------|
| 1 | Fetches company info via `fetch_stock_info()` |
| 2 | Fetches 1-month and 1-year price history |
| 3 | Computes technical indicators (RSI, MACD, Bollinger, SMAs) |
| 4 | Fetches quarterly/annual financial statements |
| 5 | Fetches and compares sector peers |
| 6 | Writes all data to individual JSON files in `data/` |
| 7 | Writes a timestamped full report: `{TICKER}_full_report_{timestamp}.json` |
| 8 | Builds and returns a tabbed Prefab UI dashboard |
**Example**: `analyze_stock("AAPL")`
---
### 2. `get_stock_price(ticker: str) ā str`
**Quick price lookup** ā returns a formatted text summary (no UI dashboard).
Returns current price, day change (absolute + percentage), day range, 52-week range, market cap, P/E ratio, analyst target price, and recommendation.
**Example**: `get_stock_price("RELIANCE.NS")`
**Sample output**:
```
š Apple Inc. (AAPL)
Exchange: NMS | Sector: Technology
Current Price: USD 226.84
Day Change: +1.23 (+0.55%)
Day Range: USD 224.50 - 227.90
52-Week Range: USD 164.08 - 237.49
Market Cap: 3.45T
P/E Ratio: 34.72
Target Price: USD 240.00 (buy)
```
---
### 3. `get_stock_history(ticker: str, period: str = "1y") ā str`
**Price history with interactive charts**. Fetches OHLCV data and returns an AreaChart (short-term) or LineChart (long-term) with volume bars.
| Period | Interval Used | Chart Type |
|--------|---------------|------------|
| `1d` | 5-minute | AreaChart |
| `5d` | 15-minute | AreaChart |
| `1mo` | 1-hour | AreaChart |
| `3mo` ā `1y` | Daily | LineChart |
| `2y` ā `5y` | Weekly | LineChart |
| `max` | Monthly | LineChart |
**Example**: `get_stock_history("TSLA", "6mo")`
---
### 4. `get_financials(ticker: str) ā str`
**Financial statements and ratios** ā quarterly/annual revenue & income bar charts, profit margin line charts, key ratios table, and balance sheet highlights.
**Data extracted**:
- Quarterly: Total Revenue, Net Income, Gross Profit, Operating Income
- Annual: Total Revenue, Net Income
- Margins: Gross, Operating, Net (computed as percentages)
- Balance sheet: Total Assets, Total Liabilities, Total Equity, Total Debt, Cash, Net Debt
**Example**: `get_financials("GOOGL")`
---
### 5. `compare_peers(ticker: str) ā str`
**Sector peer comparison** ā identifies peers in the same sector and compares them using a radar chart and sortable data table.
**Metrics compared**: P/E Ratio, P/B Ratio, Dividend Yield, ROE, Profit Margin, Revenue Growth, Beta.
Normalization: All metrics are scaled to 0ā100 for the radar chart using `abs(value) / max(abs(values)) * 100`.
**Example**: `compare_peers("NVDA")`
---
### 6. `get_technical_analysis(ticker: str) ā str`
**Technical indicators with buy/sell/hold signals**. Computes the following from 1-year daily data:
| Indicator | Parameters | Signal Logic |
|-----------|-----------|--------------|
| RSI | Window: 14 | > 70 ā Overbought, < 30 ā Oversold |
| MACD | Fast: 12, Slow: 26, Signal: 9 | MACD > Signal ā Bullish |
| SMA | 20, 50, 200 | SMA 50 > SMA 200 ā Golden Cross (Bullish) |
| EMA | 12, 26 | Displayed in indicator table |
| Bollinger Bands | Window: 20, Std Dev: 2 | Chart overlay |
**Overall Signal**: Weighted vote across RSI, MACD, SMA cross, and price vs SMA 20:
- `bullish_count > bearish_count + 1` ā **Buy**
- `bearish_count > bullish_count + 1` ā **Sell**
- Otherwise ā **Hold**
**Example**: `get_technical_analysis("RELIANCE.NS")`
---
### 7. `get_sector_overview(sector: str) ā str`
**Sector-level breakdown** ā shows a pie chart of market cap distribution and a sortable table of representative stocks.
**Supported sectors** (with 6 representative tickers each):
Technology, Financial Services, Healthcare, Consumer Cyclical, Energy, Communication Services, Industrials, Consumer Defensive, Basic Materials, Real Estate, Utilities
**Example**: `get_sector_overview("Technology")`
---
## Invoking the Yahoo Finance API & Writing to Files
### API Layer (`stock_data.py`)
All data fetching is centralized in [`stock_data.py`](src/stock_data.py). It uses `yfinance` to call the Yahoo Finance API and returns typed **dataclasses**:
| Function | Returns | API Call |
|----------|---------|----------|
| `fetch_stock_info(ticker)` | `StockInfo` | `yf.Ticker(ticker).info` |
| `fetch_price_history(ticker, period)` | `PriceHistory` | `yf.Ticker(ticker).history(period, interval)` |
| `compute_technical_analysis(ticker)` | `TechnicalAnalysis` | `yf.Ticker(ticker).history("1y", "1d")` + `ta` library |
| `fetch_financials(ticker)` | `FinancialData` | `.quarterly_financials`, `.financials`, `.balance_sheet` |
| `fetch_peer_comparison(ticker, max_peers)` | `PeerData` | `yf.Ticker().info` for each peer |
**Key design decisions**:
- All values pass through `_safe_get()` which handles `None`, `NaN`, and `Inf` safely
- Large numbers are formatted with `_format_large_number()` ā `"3.45T"`, `"150.20B"`, `"3.20M"`
- Each dataclass has a `.to_dict()` method for JSON serialization
### File Persistence Layer (`file_writer.py`)
Every tool call persists its data to the [`data/`](data/) directory via [`file_writer.py`](src/file_writer.py):
```python
write_data(ticker, "info", info.to_dict()) # ā data/AAPL_info.json
write_data(ticker, "history", data, period="1mo") # ā data/AAPL_history_1mo.json
write_data(ticker, "analysis", analysis.to_dict()) # ā data/AAPL_analysis.json
write_data(ticker, "financials", financials.to_dict()) # ā data/AAPL_financials.json
write_data(ticker, "peers", peers.to_dict()) # ā data/AAPL_peers.json
write_analysis_report(ticker, full_report) # ā data/AAPL_full_report_20260829_191739.json
```
**File naming**: `{TICKER}_{data_type}[_{period}].json` ā dots in tickers (e.g., `.NS`) are replaced with underscores.
**Full reports** include a timestamp suffix: `{TICKER}_full_report_{YYYYMMDD_HHMMSS}.json`
### Resulting Data Directory
```
data/
āāā AAPL_info.json # Company info snapshot
āāā AAPL_history_1mo.json # 1-month OHLCV data
āāā AAPL_history_1y.json # 1-year OHLCV data
āāā AAPL_analysis.json # Technical indicators + signals
āāā AAPL_financials.json # Income statements + balance sheet
āāā AAPL_peers.json # Peer comparison metrics
āāā AAPL_full_report_20260829_191739.json # Comprehensive timestamped report
āāā dashboard.html # Last-generated interactive dashboard
```
---
## Dynamic UI with Prefab UI
### How It Works
Each tool (except `get_stock_price`) returns an interactive dashboard built with **[Prefab UI](https://github.com/PrefectHQ/prefab-ui)** ā a Python library for composing rich HTML dashboards using a declarative context-manager pattern.
The flow is:
```
Tool function
āāā build_*_dashboard() (src/ui/dashboard.py)
āāā Compose UI with component helpers (src/ui/components.py)
āāā PrefabApp context manager ā in-memory HTML app
āāā _app_to_json(app)
āāā app.to_json() ā MCP transport (JSON over stdio)
āāā app.html() ā data/dashboard.html (browser viewable)
```
### Dashboard Builder (`dashboard.py`)
[`dashboard.py`](src/ui/dashboard.py) contains 6 builder functions that compose `PrefabApp` instances:
| Builder Function | Used By | Layout |
|-----------------|---------|--------|
| `build_full_dashboard()` | `analyze_stock` | Tabbed: Price Charts, Technical Analysis, Financials, Peers |
| `build_price_dashboard()` | `get_stock_history` | Header + price chart + volume bars |
| `build_technical_dashboard()` | `get_technical_analysis` | Header + RSI/MACD/Bollinger/SMA panels |
| `build_financials_dashboard()` | `get_financials` | Header + ratios table + financial charts |
| `build_peers_dashboard()` | `compare_peers` | Header + radar chart + comparison table |
| `build_sector_dashboard()` | `get_sector_overview` | Pie chart + sector stocks table |
### Reusable Components (`components.py`)
[`components.py`](src/ui/components.py) provides 7 component builder functions:
| Component Function | Prefab Widgets Used |
|-------------------|---------------------|
| `stock_header_card()` | `Card`, `Badge`, `Metric`, `Progress` (day & 52w range bars), `Row`, `Grid` |
| `key_metrics_row()` | `Grid` of `Card` + `Metric` (Market Cap, P/E, EPS, Div Yield, Beta, Volume) |
| `key_ratios_card()` | `DataTable` with 10 financial ratios |
| `price_chart()` | `AreaChart` (short-term) or `LineChart` (long-term) + `BarChart` (volume) |
| `technical_analysis_panel()` | `Ring` (RSI gauge), `BarChart` (MACD histogram), `LineChart` (Bollinger + SMA), `DataTable` (indicator values) |
| `financials_section()` | `BarChart` (revenue/income), `LineChart` (margins), `DataTable` (balance sheet) |
| `peer_comparison_section()` | `RadarChart` (normalized metrics), `DataTable` (detailed comparison) |
### Viewing Dashboards
Dashboards are accessible in three ways:
1. **MCP Client UI**: MCP hosts that support Prefab UI render the dashboard inline
2. **Browser via test client**: The test client starts a local HTTP server on `http://localhost:8765/dashboard.html`
3. **Standalone Prefab serve**:
```bash
uv run prefab serve src/ui/dashboard.py
```
---
## Client Details
### Test Client (`test_client.py`)
The project includes a fully functional **interactive MCP client** in [`test_client.py`](test_client.py) that demonstrates the complete client-side MCP flow:
#### What It Does
1. **Starts a local HTTP server** on port `8765` to serve `data/dashboard.html`
2. **Launches the MCP server** as a subprocess via `StdioServerParameters`
3. **Connects over stdio** using `stdio_client()` and `ClientSession`
4. **Lists all available tools** with their parameters
5. **Provides an interactive REPL** for calling tools
#### Connection Code
```python
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
server_params = StdioServerParameters(
command=sys.executable, # Current Python interpreter
args=["-m", "src.server"], # Run the server as a module
cwd=".",
)
async with stdio_client(server_params) as (read, write):
async with ClientSession(read, write) as session:
await session.initialize()
# List tools
tools_result = await session.list_tools()
# Call a tool
result = await session.call_tool("analyze_stock", {"ticker": "AAPL"})
```
#### Running the Client
```bash
uv run python test_client.py
```
#### Interactive Usage
```
[*] Connecting to MCP Stock Analysis Server...
[OK] Connected!
[UI] Local Dashboard Server: http://localhost:8765/dashboard.html
[TOOLS] Available tools (7):
1. analyze_stock
Params: ticker*: string
2. get_stock_price
Params: ticker*: string
...
==================================================
Type a tool name and arguments to call it.
Examples:
analyze_stock ticker=AAPL
get_stock_price ticker=RELIANCE.NS
get_stock_history ticker=TSLA period=6mo
get_sector_overview sector=Technology
Type 'list' to see tools again, 'quit' to exit.
==================================================
>> analyze_stock ticker=AAPL
[...] Calling analyze_stock({'ticker': 'AAPL'})...
š [Prefab UI Dashboard Generated!]
View Live in Browser š http://localhost:8765/dashboard.html
```
When a tool returns Prefab UI JSON (detected via `"$prefab"` key), the client:
- Prints a message pointing to the dashboard URL
- Automatically opens the dashboard in your default browser
---
## Plugin as a Skill to Claude Desktop
### Claude Desktop Configuration
Add the following to your Claude Desktop config file:
- **macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json`
- **Windows**: `%APPDATA%\Claude\claude_desktop_config.json`
```json
{
"mcpServers": {
"stock-analysis": {
"command": "uv",
"args": [
"run",
"--directory", "/absolute/path/to/MCP",
"python", "-m", "src.server"
]
}
}
}
```
> **Important**: Replace `/absolute/path/to/MCP` with the actual absolute path to this project directory.
After adding the config:
1. Restart Claude Desktop
2. You should see a š icon indicating the MCP server is connected
3. Ask Claude to analyze stocks ā it will automatically discover and use the 7 tools
### Other MCP Hosts
For other MCP-compatible hosts, add to your MCP configuration:
```json
{
"stock-analysis": {
"command": "uv",
"args": [
"run",
"--directory", "/absolute/path/to/MCP",
"python", "-m", "src.server"
],
"transport": "stdio"
}
}
```
### Example Prompts for Claude
Once the server is connected, try these prompts:
- *"Analyze Apple stock and show me the full dashboard"*
- *"What's the current price of RELIANCE.NS?"*
- *"Show me Tesla's price history for the last 6 months"*
- *"Compare NVDA with its sector peers"*
- *"Run a technical analysis on MSFT"*
- *"Give me an overview of the Technology sector"*
---
## uv Build & Run
### Install Dependencies
```bash
# Navigate to the project directory
cd MCP
# Sync all dependencies (creates .venv automatically)
uv sync
```
This reads [`pyproject.toml`](pyproject.toml) and installs:
| Dependency | Version | Purpose |
|-----------|---------|---------|
| `mcp[cli]` | ā„ 1.0.0 | MCP server SDK + CLI tools |
| `prefab-ui` | ā„ 0.5.0 | Interactive dashboard UI framework |
| `yfinance` | ā„ 0.2.40 | Yahoo Finance API wrapper |
| `pandas` | ā„ 2.0.0 | Data manipulation |
| `numpy` | ā„ 1.24.0 | Numerical computations |
| `ta` | ā„ 0.11.0 | Technical analysis indicators (RSI, MACD, Bollinger, etc.) |
### Run the MCP Server
```bash
# Option 1: Run with MCP Inspector (interactive testing UI in the browser)
uv run mcp dev src/server.py
# Option 2: Run directly via stdio transport (for AI assistants)
uv run python -m src.server
# Option 3: Use the console script alias
uv run stock-mcp
```
### Run the Test Client
```bash
uv run python test_client.py
```
### Preview Dashboard Standalone
```bash
uv run prefab serve src/ui/dashboard.py
```
### Build a Distributable Wheel
```bash
# Build the wheel package
uv build
# The wheel is output to dist/
# dist/mcp_stock_analysis-1.0.0-py3-none-any.whl
```
### Alternative: Install with pip
```bash
# Install in editable mode
pip install -e .
# Run the server
python -m src.server
```
---
## Ticker Format
| Market | Format | Example |
|--------|--------|---------|
| US (NYSE / NASDAQ) | Plain symbol | `AAPL`, `TSLA`, `MSFT`, `GOOGL` |
| India (NSE) | Symbol + `.NS` | `RELIANCE.NS`, `TCS.NS`, `INFY.NS` |
| India (BSE) | Symbol + `.BO` | `TATAMOTORS.BO`, `SBIN.BO` |
---
## Project Structure
```
MCP/
āāā pyproject.toml # Project config, dependencies, build system, console scripts
āāā uv.lock # Locked dependency versions
āāā README.md # This file
āāā test_client.py # Interactive MCP client (stdio transport + local dashboard server)
āāā data/ # Persisted stock data (auto-created, JSON files + dashboard.html)
ā āāā .gitkeep
ā āāā AAPL_info.json
ā āāā AAPL_history_1y.json
ā āāā AAPL_analysis.json
ā āāā AAPL_financials.json
ā āāā AAPL_peers.json
ā āāā AAPL_full_report_*.json
ā āāā dashboard.html # Last-generated interactive dashboard
āāā src/
āāā __init__.py # Package marker
āāā server.py # MCPServer definition + 7 @mcp.tool() functions
āāā stock_data.py # yfinance data fetcher (5 functions, 5 dataclasses)
āāā file_writer.py # JSON persistence to data/ directory
āāā ui/
āāā __init__.py # Package marker
āāā dashboard.py # 6 PrefabApp builder functions
āāā components.py # 7 reusable Prefab UI component builders
```
---
## Tech Stack
| Layer | Technology | Role |
|-------|-----------|------|
| MCP Server | `mcp` (FastMCP Python SDK) | Tool registration, stdio transport, protocol handling |
| Data Source | `yfinance` | Yahoo Finance API wrapper for stock data |
| Technical Analysis | `ta` + `pandas` + `numpy` | RSI, MACD, Bollinger Bands, SMA/EMA computation |
| UI Framework | `prefab-ui` (PrefectHQ) | Declarative interactive dashboards (charts, tables, gauges) |
| File Storage | JSON (stdlib) | Structured data persistence to `data/` directory |
| Build System | `hatchling` | PEP 517 build backend |
| Package Manager | `uv` | Fast dependency resolution and virtual environment management |
---
## License
MIT
TDQS
Scored across 7 tools
Individual tools target distinct data types (price, history, financials, technicals, peers, sector), but analyze_stock is an umbrella that duplicates all of them. Descriptions clarify intent, yet an agent may still wonder whether to call the comprehensive tool or the specialized ones.
All tool names use snake_case with a verb-first pattern (analyze_stock, get_stock_price, get_stock_history, compare_peers, etc.). The convention is consistent and predictable across the set.
Seven tools is well-scoped for a stock analysis server, covering core retrieval tasks without excessive fragmentation. Each tool has a clear role and none feels redundant beyond the umbrella analysis tool.
Core coverage is strong: current price, history, financials, technicals, peer comparison, sector overview, and a comprehensive analysis tool. Minor gaps include standalone tools for company profile, news, dividends, or earnings, but these are workable for the stated purpose.