finviz-screener-mcp
finviz-screener-mcp
FastMCP server that exposes only Finviz’s stock screener via finvizfinance.
US-only: every screen() call hardcodes Country=USA. No news, maps, insider trading, or Elite API — just screener filters → structured JSON.
Tools
Tool | Purpose |
| Progressive discovery (index / one category / one section / search) — avoids dumping ~1694 values |
| Compact screener results (default limit 25, max 100; offset capped at 400; null fields and the always-USA |
Both tools are annotated read-only (readOnlyHint) so clients can skip confirmation prompts. Invalid input and upstream failures are raised as MCP tool errors (isError: true) with messages like [invalid_filter] ... or [upstream] ..., so agents can self-correct.
Keep agent context small
list_filters() → category names + counts only
list_filters(category="Sector") → values for Sector only
list_filters(section="signals") → signals only
list_filters(query="rsi") → matching names
screen(..., limit=15, table="custom", columns=[...]) → only needed fieldsExample screen call:
{
"filters": {
"Sector": "Technology",
"Market Cap.": "Large ($10bln to $200bln)"
},
"table": "custom",
"order": "Market Cap.",
"ascend": false,
"columns": ["Ticker", "Market Cap.", "P/E", "RSI", "Price"],
"limit": 15
}Important caveats
Unofficial scrape.
finvizfinanceparses the free Finviz HTML screener. There is no free official API; Elite’s CSV/API export is a separate paid surface and is not used here.Quotes on the free site are delayed.
Scraping can break if Finviz changes its DOM, and may be against Finviz Terms of Service. Use responsibly.
Proxy fallback (for hosted/cloud IPs)
Datacenter hosts (e.g. Horizon) are often IP-blocked by Finviz (screen → HTTP 403), even though the same code works fine from a home connection. screen() handles this automatically:
Reuse the proxy that served the previous request, if there is one — no probing needed.
Try the direct connection (the only path taken locally). Once it fails, it's skipped for
FINVIZ_DIRECT_COOLDOWNseconds (default 600) so a blocked host stops paying the timeout on every call, then retried in case the block was temporary.Rotate through free proxies from a handful of actively-maintained GitHub lists (
proxy_pool.py), pulling fresh lists as older ones are used up, until a request lands orFINVIZ_FETCH_BUDGETseconds (default 45) elapse.
Most free proxies are dead, so trying them one at a time would spend a full connect timeout per corpse. Instead the pool probes candidates concurrently against Finviz itself (40 at a time) and only hands back ones that answered HTTP 200 — which also screens out proxies Finviz has already blocked. In practice a cold rotation finds a working route in ~15s and subsequent calls reuse it in ~5s.
No configuration needed. This is still best-effort — free proxies are inherently unreliable. For a guaranteed hosted fix, point finvizfinance.util.set_proxy at a paid residential proxy instead (datacenter proxies tend to get 403'd same as the host itself).
Local setup
Requires Python 3.11+ (3.12 recommended; see .python-version).
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -r requirements.txtRun the server (stdio):
fastmcp run main.py:mcp
# or
python main.pyInspect tools:
fastmcp inspect main.py:mcpCursor / Claude Desktop (stdio)
{
"mcpServers": {
"finviz-screener": {
"command": "uv",
"args": ["run", "--with-requirements", "requirements.txt", "fastmcp", "run", "main.py:mcp"],
"cwd": "/absolute/path/to/finviz-screener-mcp"
}
}
}Or with an activated venv:
{
"mcpServers": {
"finviz-screener": {
"command": "/absolute/path/to/finviz-screener-mcp/.venv/bin/fastmcp",
"args": ["run", "main.py:mcp"],
"cwd": "/absolute/path/to/finviz-screener-mcp"
}
}
}No API key or .env is required.
Deploy on Prefect Horizon (FastMCP Cloud)
Horizon is the managed host from the FastMCP team (docs; console: horizon.prefect.io). URLs look like https://{name}.fastmcp.app/mcp.
Push this repo to GitHub.
Sign in at horizon.prefect.io with GitHub and select the repo.
Configure:
Entrypoint:
main.py:mcpDependencies:
requirements.txt(auto-detected)
Deploy. Horizon ignores
if __name__ == "__main__"and installs deps fromrequirements.txt.Connect clients to
https://your-server-name.fastmcp.app/mcp.
No environment variables or secrets are required for this server. If you add any later: Settings → Environment Variables, mark sensitive, then rebuild and redeploy (env changes do not hot-reload).
Smoke check
python -c "
from main import list_filters, screen
print([c['name'] for c in list_filters()['categories'][:5]])
print(screen(filters={'Sector': 'Technology'}, limit=3)['count'])
"