A local AI assistant layer that lets Claude Desktop talk directly to Market Pulse — querying predictions, sentiment, news, and political trades via natural language.
Overview
┌─────────────────────────────────────────────────────────────────────┐
│ Developer / Analyst │
│ "What does the model predict for NVDA next week?" │
│ "Show me recent congressional buys in tech stocks" │
└──────────────────────────────┬──────────────────────────────────────┘
│ natural language query
▼
┌─────────────────────────────────────────────────────────────────────┐
│ Claude Desktop │
│ (Claude Sonnet / Opus) │
│ │
│ understands the question → picks the right tool → formats answer │
└──────────────────────────────┬──────────────────────────────────────┘
│ MCP tool call (stdio)
▼
┌─────────────────────────────────────────────────────────────────────┐
│ mcp_server/server.py │
│ (FastMCP, runs locally) │
│ │
│ list_tickers get_prediction get_all_predictions │
│ get_sentiment get_news get_political_trades │
│ get_price_history get_debate get_prediction_accuracy │
└──────────────────────────────┬──────────────────────────────────────┘
│ SQLAlchemy queries
▼
┌─────────────────────────────────────────────────────────────────────┐
│ Supabase PostgreSQL (prod) │
│ SQLite (local dev fallback) │
│ │
│ predictions · news_articles · sentiment_scores │
│ stock_prices · political_trades · tickers │
└─────────────────────────────────────────────────────────────────────┘
| What |
Why it matters |
| Natural language interface |
No SQL, no dashboard navigation — just ask |
| 9 tools over live DB |
Answers always reflect the current state of the data |
| Runs locally via stdio |
No server to deploy; no API keys to expose to the internet |
| Claude Desktop picks the tool |
You don't need to know which tool maps to which question |
How It Works
Step 1 — You ask Claude Desktop a question
You ──► "Which of our Korean tickers has the most bullish sentiment this week?"
or
"Run a bull/bear debate for Samsung (005930)"
or
"What's NVDA's prediction accuracy for 7-day horizon?"
- Input: natural language question typed in Claude Desktop
- Output: question sent to Claude with the MCP tool list available
- Who does it: Claude Desktop (local app)
Question ──► Claude reasons about which tool fits ──► tool call
Examples:
"sentiment for AAPL" ──► get_sentiment(ticker="AAPL", days=7)
"recent TSLA news" ──► get_news(ticker="TSLA", days=3)
"Pelosi trades" ──► get_political_trades(trader="Pelosi")
"all predictions today" ──► get_all_predictions(horizon="1d")
"NVDA price last 30 days" ──► get_price_history(ticker="NVDA", days=30)
"bull/bear case for MSFT" ──► get_debate(ticker="MSFT")
- Input: your question + list of 9 available tools
- Output: structured tool call parameters
- Who does it: Claude (the LLM reasoning layer)
Step 3 — MCP server executes the query
Tool call arrives via stdin (stdio transport)
│
▼
mcp_server/server.py (FastMCP)
│
├── opens a DB session (SessionLocal → Supabase or SQLite)
├── runs SQLAlchemy query
├── formats result as JSON string
└── returns JSON via stdout
DB is read-only — the MCP server never writes data
- Input: validated tool call (name + params)
- Output: JSON string with query results
- Who does it:
mcp_server/server.py
Raw JSON ──► Claude synthesizes ──► human-readable response
Example output:
"Samsung (005930) sentiment over the past 7 days:
- Average score: +0.31 (positive)
- 14 articles scored; 9 positive, 3 neutral, 2 negative
- Strongest signal: Q3 earnings beat (score +0.72)"
- Input: JSON tool result
- Output: formatted, conversational answer in Claude Desktop
- Who does it: Claude (the LLM synthesis layer)
┌───────────────────────┬──────────────────────────────────────────┐
│ Tool │ What it fetches │
├───────────────────────┼──────────────────────────────────────────┤
│ list_tickers │ All tracked symbols (US + KR) │
│ get_prediction │ ML forecast for one ticker + horizon │
│ get_all_predictions │ Latest forecasts for every ticker │
│ get_sentiment │ News sentiment summary for a ticker │
│ get_news │ Recent headlines with sentiment scores │
│ get_political_trades │ Congressional + insider trades │
│ get_price_history │ OHLCV bars for a ticker │
│ get_debate │ Multi-agent bull/bear verdict │
│ get_prediction_accuracy│ Historical accuracy stats per ticker │
└───────────────────────┴──────────────────────────────────────────┘
Components
Claude Desktop
│ stdio (stdin/stdout pipe)
▼
mcp_server/
└── server.py ──── FastMCP app, 9 tools registered ────► DB
│
├── db/database.py (SessionLocal)
├── db/models.py (ORM models)
├── sentiment/analyzer.py
└── models/predictor.py / debate.py
| Component |
What it does |
Lives in |
server.py |
FastMCP app — registers and serves 9 tools |
mcp_server/server.py |
SessionLocal |
Opens/closes DB connections per tool call |
db/database.py |
get_ticker_sentiment_summary() |
Aggregates sentiment for get_sentiment tool |
sentiment/analyzer.py |
get_latest_predictions() |
Bulk prediction query for get_all_predictions |
models/predictor.py |
debate() |
Multi-agent bull/bear analysis for get_debate |
models/debate.py |
Data Flow
You (Claude Desktop)
│
│ "show me the bull/bear debate for AAPL"
▼
Claude (reasoning)
│ tool call: get_debate(ticker="AAPL", market="US")
▼ via stdio
mcp_server/server.py
│ db = SessionLocal()
│ result = debate(db, "AAPL", "US")
│
│ debate() internally reads:
│ ├── latest Prediction row (ML signal)
│ ├── SentimentScore avg (7d) (news mood)
│ ├── StockPrice (RSI, momentum) (technicals)
│ └── generates bull case + bear case + verdict via Claude Haiku
│
│ db.close()
│ return JSON { bull, bear, verdict, confidence }
▼
Claude (synthesis)
│
│ formats into readable debate summary
▼
You (see the answer)
Setup
# 1. Run the MCP server (it stays alive as long as Claude Desktop is open)
python -m mcp_server.server
# 2. Register it in Claude Desktop (~/.claude/claude_desktop_config.json):
{
"mcpServers": {
"market-pulse": {
"command": "python",
"args": ["-m", "mcp_server.server"],
"cwd": "/path/to/fintrack"
}
}
}
# The MCP server connects to whatever DATABASE_URL is in .env
# (Supabase in prod, SQLite in local dev — automatic fallback)
Key Decisions
| Decision |
Chosen approach |
Why |
| Transport |
stdio (not HTTP) |
No port conflicts; Claude Desktop handles the process lifecycle |
| DB access |
Direct SQLAlchemy, read-only |
Fastest path; no separate HTTP hop through the FastAPI layer |
| Tool count |
9 tools (no more) |
More tools = more token overhead for Claude to reason through; kept focused |
get_debate live |
Runs inference at call time |
Debate is expensive — only run when explicitly asked, not cached |
| Market filter param |
Optional on most tools |
Works for both US-only and mixed US+KR queries |
What Can Go Wrong
| Failure |
Impact |
How we handle it |
DATABASE_URL not set |
Tools return empty or SQLite fallback data |
Check .env; MCP server logs the connection string on start |
| DB connection pool exhausted |
Tool calls hang or error |
Each tool opens and closes its own session; no leaks by design |
get_debate slow |
Claude Desktop appears to hang |
Debate calls Claude Haiku internally — takes 3–8s; normal |
| MCP server not registered |
Claude Desktop shows no market-pulse tools |
Verify claude_desktop_config.json path and cwd |
| Stale predictions |
Forecasts look old |
Predictions refresh every 6h via the scheduler; check run_predictions job |
| No sentiment data |
get_sentiment returns score: null |
News scraper may have missed a cycle; check fetch_us_news / fetch_kr_news logs |
Relationship to the Rest of the System
┌─────────────────────────────┐
│ Market Pulse System │
│ │
┌──────────────┐ │ ┌─────────────────────────┐│
│ Claude │ │ │ FastAPI (port 8000) ││
│ Desktop │ │ │ React dashboard reads ││
│ │◄──┤ │ data from here ││
│ uses MCP │ │ └─────────────────────────┘│
│ tools for │ │ │ │
│ ad-hoc │ │ ┌──────────▼──────────────┐│
│ queries │ │ │ Supabase PostgreSQL ││
└──────────────┘ │ └──────────▲──────────────┘│
│ │ │
┌──────────────┐ │ ┌──────────┴──────────────┐│
│ MCP Server │───┤ │ APScheduler (15 jobs) ││
│ (stdio) │ │ │ fills the DB every ││
└──────────────┘ │ │ 15min – 24h ││
│ └─────────────────────────┘│
└─────────────────────────────┘
MCP Server ≠ the API
- FastAPI serves the React dashboard (port 8000, HTTP)
- MCP Server serves Claude Desktop (stdio, no port)
- Both read the SAME database
Glossary
| Term |
Plain English definition |
| MCP (Model Context Protocol) |
A standard way for AI apps like Claude Desktop to call tools — like plugins, but for AI |
| FastMCP |
A Python library that makes it easy to build an MCP server; handles the protocol wiring |
| stdio transport |
The MCP server communicates by reading from stdin and writing to stdout — like a command-line pipe, no network involved |
| Tool |
A named function the AI can call, like get_prediction(ticker="AAPL") — the AI decides when to call it |
| Claude Desktop |
The desktop app version of Claude that supports MCP tool plugins |
get_debate |
A tool that triggers a live multi-agent discussion — one simulated bull, one simulated bear, with a verdict |
| SessionLocal |
The SQLAlchemy function that opens a database connection; each tool call opens one and closes it when done |
DATABASE_URL |
The connection string that points the MCP server at either Supabase (production) or a local SQLite file (development) |
| Horizon |
The forecast time window: 1d = tomorrow, 7d = next week, 30d = next month |
| Sentiment score |
A number from -1.0 (very negative news) to +1.0 (very positive news) computed from news headlines |
| Political trades |
Buys and sells by US Congress members (required by the STOCK Act to be disclosed within 45 days) |
Last updated: 2026-06-02 · Owner: Michael Ko