options-chain-mcp
by blake365
README.md
# options-chain-mcp
Read-only MCP server for options research. Pluggable data provider — currently supports [Tradier](https://documentation.tradier.com/brokerage-api) and [Alpaca](https://docs.alpaca.markets).
[](https://www.npmjs.com/package/options-chain-mcp)
```bash
npx -y options-chain-mcp
```
> **You must bring your own market data credentials.** Unlike a server that wraps a
> free public API, this one talks to a brokerage data provider on your behalf, so it
> needs a `TRADIER_TOKEN`, or an `ALPACA_API_KEY_ID` + `ALPACA_SECRET_KEY`, in its
> environment. Free sandbox/paper accounts work fine — see
> [Choosing a data provider](#choosing-a-data-provider). Started without credentials,
> the server exits immediately and prints which variable is missing.
## Overview
This Model Context Protocol (MCP) server gives AI assistants the tools to research options, without any ability to place trades:
- **`find-options-chain`** — chain for a symbol + expiration, filtered to options with real volume/bid/ask and strikes within a percentage of spot. Automatically trims to significant strikes to keep the payload LLM-friendly.
- **`find-option-expirations`** — valid expiration dates for an underlying.
- **`get-quote`** — latest quote for a stock symbol or a single OCC option contract. Includes Greeks + IV when the provider supports them (Alpaca does; Tradier requires a chain lookup).
- **`historical-prices`** — OHLCV bars for a stock or OCC option over any range and interval.
The server runs two ways:
- **Locally over stdio** for Claude Desktop
- **Remotely on Cloudflare Workers** with OAuth 2.1, for Claude.ai and other MCP clients that speak the remote-connector protocol
Please note:
- Requires API credentials for whichever provider you use (free paper/sandbox accounts work)
- Market data on free plans is typically 15-min delayed
- Intended for informational purposes only
Users can run queries using natural language.
## Why this server (instead of the official Tradier or Alpaca MCP)
**Built for LLM context budgets.** A typical underlying has hundreds of strikes across dozens of expirations. Calling a raw `get_option_chain` from the official Alpaca server returns *all of them* — every dead far-OTM strike, every penny-bid contract, every field the API exposes. That payload eats thousands of tokens per call and forces the model to filter the noise itself. This server filters server-side:
- Strikes are pre-filtered to a configurable percentage band around spot
- Dead contracts are dropped (`volume > 0 OR open_interest > 0` and `bid/ask > $0.10`)
- A "significant strikes" pass keeps round-numbered strikes at distance and finer-grained strikes near the money — mimicking how a human trader actually reads a chain
- Only the fields a research workflow needs are returned (no exchange codes, no raw timestamps, no boilerplate)
In practice a NVDA chain comes back as ~15 strikes instead of ~400 — roughly **20–40× fewer tokens per call**, leaving room for actual analysis in the conversation.
**Other things going for it:**
- **Read-only by design.** No `place_order`, no `close_position`, no `exercise_options_position`. Point an LLM at it without worrying about hallucinated trades.
- **Provider-pluggable.** Tradier and Alpaca behind one identical tool surface. Swap with one env var; if one provider's data goes stale or the account expires, fall over to the other without changing prompts or client config.
- **Remote OAuth deployment included.** Works from claude.ai web (and any other OAuth-aware MCP client), not just locally-connected Claude Desktop. The official Alpaca server is stdio-only.
## Sample Output
**Q: What are the highlights of the SPY options chain for two weeks out?**
**A:** I'll check the current SPY options chain for two weeks out from today.
View Result from find-options-chain from options-chain (local)
```json
{
"symbol": "SPY",
"expiration": "2025-03-28"
}
```
Now that I have the SPY options chain for March 28, 2025 (two weeks out), I'll highlight the key information:
### SPY Options Chain Highlights (March 28, 2025 Expiration)
Current Market Context
Current SPY price appears to be around $565-566 based on the at-the-money options
### Call Options Highlights
1. Highest Volume:
- $590 calls with 217 contracts traded
- $600 calls with 174 contracts traded
- $571 calls with 130 contracts traded
2. Highest Open Interest:
- $600 calls: 8,299 contracts
- $590 calls: 6,591 contracts
- $605 calls: 4,676 contracts
- $580 calls: 4,596 contracts
3. Notable Activity:
- Far out-of-the-money calls ($590-$600) showing significant volume, suggesting some traders are positioning for potential upside movement
- The $590 calls (priced at $1.07-$1.09) have high volume and open interest
### Put Options Highlights
1. Highest Volume:
- $500 puts with 217 contracts traded
- $460 puts with 160 contracts traded
- $510 puts with 163 contracts traded
2. Highest Open Interest:
- $525 puts: 79,196 contracts (extremely high)
- $530 puts: 7,060 contracts
- $550 puts: 4,883 contracts
- $570 puts: 4,337 contracts
3. Notable Activity:
- The massive open interest in $525 puts is striking, suggesting a significant hedging position
- Overall put activity shows traders protecting against downside risk
### Implied Volatility Indicators
- Looking at the pricing, implied volatility appears moderate
- The put-call ratio based on open interest is skewed toward puts, suggesting market participants are hedging against downside risk
### Options Skew
- There's higher open interest in OTM puts than OTM calls, indicating traders are more concerned about downside protection than upside speculation
## Installation
Requires [Node.js](https://nodejs.org/en/download) 20 or newer. No install step is
needed — `npx` fetches the server on first run. You do need credentials for one of the
two data providers; see [Choosing a data provider](#choosing-a-data-provider).
There are two ways to run this server, and they authenticate differently:
| | Local (stdio, `npx`) | Remote (Cloudflare Workers) |
| --- | --- | --- |
| Client authentication | none — runs as a local subprocess | OAuth 2.1 with a passcode |
| Provider credentials | **yours**, in the client's `env` config | **yours**, as Worker secrets |
| Who it suits | you, on your own machine | you across devices, or people you share the passcode with |
| Whose API quota | the local user's | the deploying account's |
Local setup is covered directly below; remote setup is under
[Running on Cloudflare Workers](#running-on-cloudflare-workers-remote-oauth-21).
### Claude Code
```bash
claude mcp add options-chain --env TRADIER_TOKEN=your_token -- npx -y options-chain-mcp
```
### Any other MCP client
The server speaks MCP over STDIO. Run it with `npx -y options-chain-mcp`, or install it
globally with `npm install -g options-chain-mcp` and run `options-chain-mcp`, with the
provider credentials present in the environment.
### Development
```bash
npm install # install dependencies
npm run dev:stdio # run the stdio server from source
npm run build # compile TypeScript to build/
npm run typecheck # type-check both the stdio and Workers configs
npm run dev # run the Cloudflare Workers version locally
npm run deploy # deploy the Workers version
```
Tool definitions live in `src/tools.ts` and are shared by both entry points:
`src/index.ts` (stdio) and `src/worker.ts` (Cloudflare Workers). Provider-specific
code lives in `src/providers/`.
`build/` is generated and not checked in — run `npm run build` before pointing Claude
Desktop at a local build.
## Choosing a data provider
The server supports two providers. Set the credentials for whichever you want and (optionally) `DATA_PROVIDER` to pick explicitly. If `DATA_PROVIDER` is unset, Alpaca is used when both Alpaca keys are set, otherwise Tradier.
| Provider | Env vars | Notes |
| --- | --- | --- |
| Tradier sandbox | `TRADIER_TOKEN` | 15-min delayed. Sandbox tokens expire — [dashboard](https://sandbox.tradier.com) lets you regenerate. |
| Alpaca | `ALPACA_API_KEY_ID`, `ALPACA_SECRET_KEY` | Paper account key works fine. Free `indicative` options feed includes Greeks + IV. |
See `sample.env` for optional overrides (feed selection, base-URL overrides for live accounts).
### Provider differences worth knowing
| Field | Tradier | Alpaca |
| --- | --- | --- |
| `volume` (per option) | Daily total | `null` — not exposed by the snapshot endpoint |
| `open_interest` | Returned natively | Fetched from the trading API's contracts endpoint and merged in |
| Greeks + IV on free tier | Yes (when `greeks=true`) | Yes (free `indicative` feed) |
The chain filter treats `volume > 0 OR open_interest > 0` as "real market interest," so dead strikes get dropped on either provider. When you see `"volume": null` in an Alpaca response, that means *the provider doesn't report it* — not that the contract is inactive. Use `open_interest` for liquidity assessments on Alpaca.
## Connecting with Claude Desktop (stdio)
1. Open your Claude Desktop configuration at:
- macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`
- Windows: `%APPDATA%\Claude\claude_desktop_config.json`
2. Add the server configuration (using whichever provider you want):
```json
{
"mcpServers": {
"options-chain": {
"command": "npx",
"args": ["-y", "options-chain-mcp"],
"env": {
"TRADIER_TOKEN": "your_tradier_sandbox_token"
}
}
}
}
```
To run a local build instead, use `"command": "node"` with
`"args": ["/full/path/to/options-chain/build/index.js"]` after `npm run build`.
Or for Alpaca:
```json
"env": {
"ALPACA_API_KEY_ID": "your_key_id",
"ALPACA_SECRET_KEY": "your_secret"
}
```
3. Close/Quit then restart Claude Desktop
Once you restart you should see a small hammer icon in the lower right corner of the textbox. If you hover over the icon you'll see the number of MCP tools available.
> The legacy lowercase `token` env var is still accepted for backward compatibility with older configs.
## Running on Cloudflare Workers (remote, OAuth 2.1)
The Worker entrypoint (`src/worker.ts`) exposes the same tools over MCP's SSE and Streamable HTTP transports, wrapped in an OAuth 2.1 provider. Claude.ai (web and desktop) discovers the endpoints automatically via Dynamic Client Registration.
### Requirements
- A Cloudflare account on any paid Workers plan (Durable Objects are used for MCP session state)
- Wrangler authenticated: `npx wrangler login`
- A KV namespace bound as `OAUTH_KV` (create with `npx wrangler kv namespace create OAUTH_KV` and paste the ID into `wrangler.jsonc`)
### Configure secrets
For Tradier:
```bash
npx wrangler secret put TRADIER_TOKEN
```
For Alpaca:
```bash
npx wrangler secret put ALPACA_API_KEY_ID
npx wrangler secret put ALPACA_SECRET_KEY
# optional:
npx wrangler secret put DATA_PROVIDER # "alpaca" or "tradier"
```
And the auth passcode that gates the consent page:
```bash
npx wrangler secret put APPROVE_PASSCODE # e.g. `openssl rand -base64 18`
```
For local dev, put the same keys in a `.dev.vars` file at the repo root (gitignored).
### Deploy
```bash
npm run dev # local dev server with hot reload
npm run deploy # publish to <name>.<account>.workers.dev
```
### Connect Claude to the deployed server
In Claude.ai → Settings → Connectors → Add custom connector:
1. URL: `https://<your-worker>.workers.dev/sse` (or `/mcp` for Streamable HTTP).
2. Leave client ID/secret blank — the server advertises Dynamic Client Registration.
3. Save and click connect. Claude opens a consent page; enter the `APPROVE_PASSCODE` you set above.
4. You'll be redirected back, authorized. Tokens refresh for 30 days.
### Auth upgrade path
For multi-user access or SSO, put **Cloudflare Access** in front of the Worker route. The OAuth flow still works for the MCP client; Access just adds a second layer for the human consent page.
## Troubleshooting
If Claude Desktop cannot find `npx`, provide its full path (on macOS, typically
`/usr/local/bin/npx` or `/opt/homebrew/bin/npx`). The same applies to `node` if you are
running a local build.
If the server does not appear at all, check Claude Desktop's logs — the server now
writes a specific reason to stderr (for example, missing provider credentials) instead
of exiting silently.TDQS
A3.7/5.0
Scored across 4 tools
Disambiguation5/5
Each tool has a clearly distinct purpose: historical bars, expiration listing, latest quote with Greeks, and options chain query. No overlap in functionality.
Naming Consistency4/5
Three tools follow verb_noun pattern (find-option-expirations, get-quote, find-options-chain), but historical-prices is a noun phrase without a verb, creating a minor inconsistency.
Tool Count5/5
With 4 tools, the server covers essential options data operations (historical, expirations, quotes, chain) without being too sparse or bloated.
Completeness4/5
Core data retrieval is covered, but there is a minor gap: find-options-chain does not explicitly include Greeks for options in the chain, and there is no tool for retrieving option-specific Greeks across multiple contracts in a batch.
Maintenance
ActivitySlowing
ResponsivenessNo issues