This document is a hands-on checklist for verifying the Massive Claude Code plugin works correctly. A tester should be able to complete all sections in roughly 60-90 minutes.
Before starting, confirm the following:
- Claude Code CLI installed and working (
claude --version) - Python 3.12+ installed (
python3 --version) - uv installed (
uv --version) - Node.js 16+ installed (
node --version) - Go 1.21+ installed (
go version) - (Optional) JDK 21+ and Gradle for Kotlin tests (
java -version,gradle --version) - A Massive API key (free Basic tier works for most tests; get one at massive.com/dashboard)
- Git clone of this repo
The plugin does not bundle an MCP server. Install and register it once before the live-API sections:
# Install the server binary globally via uv
uv tool install "mcp_massive @ git+https://github.com/massive-com/mcp_massive"
# Register it with Claude Code at user scope
claude mcp add massive --scope user --env MASSIVE_API_KEY=your_key -- mcp_massive-
which mcp_massivereturns a path under~/.local/bin/ -
claude mcp listshowsmassivewith status✓ Connected - Skip this subsection if you only want to test §3 (API knowledge, no MCP)
Verify the plugin loads correctly from a local directory.
claude --plugin-dir .- Claude Code starts without errors
- No warnings about missing files or invalid plugin config
- The five skills appear when you type
/massive:(tab completion or listing) -
/reload-pluginsshows: 1 plugins, 5 skills, 0 errors
The Massive MCP server is registered at user scope (see Prerequisites), so it's available in every Claude Code session — no per-plugin wiring. If you skipped the MCP setup, the tools below won't exist and §4 tests won't apply.
Prompt: What MCP tools do you have available from Massive?
- Claude lists three tools:
search_endpoints,call_api,query_data - No connection errors (
claude mcp listshows✓ Connected)
If the tools are missing: run claude mcp list. If massive is absent, go back to Prerequisites and run the claude mcp add command. If it's listed but shows an error, try mcp_massive in a terminal to smoke-test the binary — it should start and wait for stdin.
These tests verify that .claude/CLAUDE.md is loaded and Claude knows Massive conventions.
Prompt: What ticker would I use to get Bitcoin price data from Massive?
- Claude responds with
X:BTCUSD(notBTCUSDorBTC) - Claude mentions the
X:prefix is for crypto
Prompt: How do I get S&P 500 index data?
- Claude responds with
I:SPX(notSPXor^SPX)
Prompt: Show me how to get 30 days of AAPL daily bars using the Python SDK
- Code has
load_dotenv()BEFOREfrom massive import RESTClient(import ordering matters) - Code uses
RESTClient()(no hardcoded key) - Code uses
islice()to cap the generator - Code uses
list_aggs("AAPL", 1, "day", ...)with correct parameter order - Code passes
sort="asc"for chronological order - Timestamps converted with
datetime.fromtimestamp(bar.timestamp / 1000)orpd.to_datetime(..., unit="ms")(NOT raw strftime on bar.timestamp)
Prompt: I'm building a Node.js app. Show me how to get AAPL daily bars with the Massive JS SDK.
- Code uses
import { restClient } from "@massive.com/client-js" - Code uses
restClient(process.env.MASSIVE_API_KEY)(notRESTClient()) - Method is
getStocksAggregates(notlist_aggs) - ALL methods take a single object parameter:
getStocksAggregates({ stocksTicker: "AAPL", multiplier: 1, timespan: "day", ... }) - Bar fields are abbreviated:
bar.o,bar.h,bar.l,bar.c,bar.v,bar.t - Timestamps converted with
new Date(bar.t) - No Python patterns present (
RESTClient,list_aggs,load_dotenv)
Prompt: I'm writing Go. Show me how to get AAPL daily bars with the Massive Go SDK.
- Uses
rest.New("")(reads from env) withgodotenv.Load()before it - Method is
GetStocksAggregatesWithResponse(notlist_aggs) - First param is
context.Background() - Uses
rest.Ptr()for optional params - Nil-checks
resp.JSON200andresp.JSON200.Resultsbefore accessing - Bar fields:
bar.O,bar.H,bar.L,bar.C,bar.V,bar.Timestamp
Prompt: I'm building an Android app with Kotlin. Show me how to get AAPL daily bars.
- Uses
PolygonRestClient(apiKey)(NOTDefaultApi()orApiClient.apiKey) - Package is
io.polygon.kotlin.sdk.rest(NOTorg.openapitools.client) - Method is
getAggregatesBlocking(AggregatesParameters(...))(NOTgetStocksAggregates) - Bar fields use full names:
bar.open,bar.high,bar.low,bar.close,bar.volume,bar.timestampMillis - Mentions JitPack dependency (
com.github.massive-com:client-jvm:v5.1.2)
Prompt: How do I use the RSI indicator with the Massive Python SDK?
- Claude uses
get_rsi()(notlist_rsi) - Accesses result via
result.values(NOTlist(islice(...))) - Explains that
get_*methods return single objects, not iterators - Each value has
.timestampand.value
Prompt: How does pagination work in the Massive Python SDK?
- Claude explains that
list_methods return generators - Claude explains that
limitcontrols page size, not total results - Claude mentions
islice()for capping results - Claude does NOT tell the user to manually handle
next_url
Prompt: I want to get real-time stock quotes. What plan do I need?
- Claude mentions that quotes require Advanced plan ($199/mo) or higher
- Claude distinguishes between Individual and Business plans
- Claude does NOT say quotes are available on all plans
Prompt: Can I stream real-time data from the REST API?
- Claude says no, REST endpoints are polled, not streamed
- Claude points to WebSocket for real-time streaming
These tests require a valid API key with at least Basic tier access.
Prompt: Use the MCP tools to find endpoints for stock aggregates
- Claude calls
search_endpointswith a relevant query - Results include the custom bars endpoint (
/v2/aggs/ticker/{stocksTicker}/range/...)
Prompt: Call the API to get AAPL's last 5 daily bars and store the results
- Claude calls
call_apiwith the correct endpoint and parameters - Claude uses
store_asto save results as a DataFrame - Results contain OHLCV data for AAPL
Prompt: Query the stored data to show me the day with the highest volume
- Claude calls
query_datawith a SQL statement against the stored DataFrame - Returns a single row with the highest volume day
Prompt: /massive:scaffold test-project rest python
- Creates a
test-project/directory - Contains
pyproject.tomlwithmassive>=2.4.0andpython-dotenv - Contains
.env.examplewithMASSIVE_API_KEY=your_api_key_here - Contains
.gitignorewith.envexcluded - Contains
main.pywithload_dotenv()BEFOREfrom massive import RESTClient - Contains
README.mdwith quickstart (cd, cp .env.example, uv sync, uv run) - Claude mentions which plan tier the project needs
- Claude points to
/massive:discoverand/massive:debugas follow-ups - Runtime test:
cd test-project && cp .env.example .env, add key,uv sync && uv run python main.pyproduces real data
Prompt: /massive:scaffold test-js rest javascript
- Creates
package.jsonwith"@massive.com/client-js": "^10.6.0"(pinned, NOT"latest") anddotenv,"type": "module" - Creates
index.jsusingrestClient(),getStocksAggregates({...})with object params - Bar fields are abbreviated (
bar.o,bar.h, etc.) - No Python patterns (
RESTClient,list_aggs,load_dotenv) - Runtime test:
npm install && node index.jsproduces real data
Prompt: /massive:scaffold test-go rest go
- Creates
go.modwithgithub.com/massive-com/client-go/v3andgodotenv - Creates
main.gowithGetStocksAggregatesWithResponse,rest.Ptr(), nil checks - No Python patterns
- Runtime test:
go mod tidy && go run main.goproduces real data
Prompt: /massive:scaffold test-kt rest kotlin
- Creates
build.gradle.ktswithcom.github.massive-com:client-jvm:v5.1.2from JitPack - Creates
src/main/kotlin/Main.ktwithPolygonRestClient(apiKey),getAggregatesBlocking(AggregatesParameters(...)) - Bar fields are full names (
bar.open,bar.high,bar.timestampMillis) - No Python/JS patterns
- Runtime test (requires JDK):
gradle wrapper && ./gradlew runproduces real data
Prompt: /massive:scaffold ws-py websocket python
- Plan tier warning: Claude flags that WebSocket requires Starter plan ($29-49/mo) or above BEFORE scaffolding, or confirms the user has Starter
- Imports
from massive.websocket.models import Market, Feed - Uses
WebSocketClient(api_key=..., market=Market.Stocks, subscriptions=[...]) - Has handler function and
client.run(handle_msg=handler)
Prompt: /massive:scaffold ws-js websocket javascript
- Uses
websocketClient()andws.stocks()(not Python'sWebSocketClient) - Uses
JSON.stringify({ action: "subscribe", params: "T.AAPL" })
Prompt: /massive:scaffold ws-go websocket go
- Uses
massivews.New(massivews.Config{...})with channel-basedc.Output()
Prompt: /massive:scaffold ws-kt websocket kotlin
- Uses
PolygonWebSocketClientwith listener pattern, subscribes afteronAuthenticated
Prompt (with JS context): I'm building a Node.js app. /massive:discover "options Greeks for SPY"
- Shows JavaScript SDK example (not Python)
- Uses
getOptionsChain({underlyingAsset: "SPY", ...})with object params - Mentions plan tier requirements (Options Starter or above)
Prompt (no language context): /massive:discover "treasury yield curve data"
- Defaults to Python example
- Includes
load_dotenv()before import
Python error:
Prompt: /massive:debug TypeError: 'SingleIndicatorResults' object is not iterable when running: rsi = list(client.get_rsi('AAPL', params={'window': 14}))
- Identifies
get_rsi()returnsSingleIndicatorResults, not an iterator - Suggests
result.valuespattern - Explains
get_*vslist_*distinction
JavaScript error:
Prompt: /massive:debug I get 'client.list_aggs is not a function' in my Node.js code
- Identifies
list_aggsas the Python method name - Suggests
getStocksAggregates({stocksTicker: ...})with object params - Fix is in JavaScript, not Python
Go error:
Prompt: /massive:debug My Go code panics with nil pointer dereference on resp.JSON200.Results
- Suggests nil-check guard:
if resp.JSON200 != nil && resp.JSON200.Results != nil - Fix is in Go
Kotlin error:
Prompt: /massive:debug Could not resolve com.github.massive-com:client-jvm:v5.1.2 in Gradle
- Identifies missing JitPack repository
- Suggests adding
maven("https://jitpack.io")to repositories
Rate-limit error (Python):
Prompt: /massive:debug I keep getting BadResponse errors with "You've exceeded the maximum requests per minute" during a batch job
- Identifies as a 429 rate-limit (Basic tier: 5 calls/min)
- Provides
with_backoffhelper using body-text markers ("maximum requests","rate limit","too many requests") NOT"429" in str(e)(the SDK body does not include HTTP status) - Suggests caching, universal snapshot batching, or upgrading to Starter
Rate-limit retry (JS/Go):
Prompt: /massive:debug how do I retry 429s in my Node.js / Go code?
- JS example uses
err?.status ?? err?.response?.status === 429(axios error shape) - Go example uses
strings.Contains(err.Error(), "429")(SDK includes HTTP status in error)
Go Last Trade fields:
Prompt: /massive:debug In Go, resp.JSON200.Results.P is undefined after GetLastStocksTradeWithResponse
- Identifies that the generated struct names the trade price
BidPriceand sizeBidSize(JSON-taggedpands), despite being on a Trade response - Suggests using
r.BidPriceandr.BidSize(nil-check*float64)
Prompt: /massive:options "bull call spread" SPY python
- Plan tier check: Claude asks about or confirms Options Starter ($49/mo) BEFORE scaffolding (since Greeks/IV are gated)
- Creates a project DIRECTORY (not a throwaway script)
-
pyproject.tomlhasmassive>=2.4.0(NOTmassive-sdk) -
main.pyhasload_dotenv()beforefrom massive import RESTClient - Dynamic expiration dates: Expiration window is derived from
date.today()(e.g.,today + timedelta(days=7)totoday + timedelta(days=60)), NOT hardcoded past dates like"2025-06-01" - Includes
.env.example,.gitignore,README.mdwith plan-tier note - Quickstart output: cd, cp .env.example, install, run
- Calculates max profit, max loss, breakeven, risk/reward
Prompt: /massive:options "covered call" AAPL javascript
- Creates project with
package.json(pins"@massive.com/client-js": "^10.6.0") - Uses
getOptionsChain({underlyingAsset: ...})with object params - Notes Options Starter plan requirement upfront (not just at the end)
Prompt: /massive:dashboard test-dash multi-asset
- Creates modular project structure with
terminal/subdirectory -
data.py: technical indicators useresult.valuespattern (NOTlist(islice(...))) -
data.py: market status usesclient.get_market_status()SDK method (NOT raw REST) -
data.py:list_aggspassessort="asc" - No
requestslibrary in dependencies or code - Quickstart: cd, cp .env.example .env, uv sync, uv run streamlit run
Prompt: /massive:dashboard macro-dash macro
- Uses SDK methods:
list_treasury_yields(),list_inflation(),list_labor_market_indicators() - Does NOT use
requests.getor raw HTTP calls - No references to any API domain other than
api.massive.com
Clean up test directories after each test.
Cross-tool instruction files (Cursor, Copilot, Gemini, Windsurf, and others) have been moved to the standalone massive-ai-rules repo. Test them there.
Review all Claude responses from the tests above and verify:
- REST endpoints described as "polled," never "streamed"
- Individual tickers not called "markets" (markets = asset classes)
- No claim that one endpoint covers all asset classes
- No emojis in any output (including Streamlit
page_icon) - No em dashes in any output (use commas, periods, semicolons, parentheses)
- No mention of "stock data app" (should be "financial dashboard," "quantitative analysis," etc.)
- No references to "polygon.io" in any generated code or output
- All API URLs use
api.massive.com - Package name is
massive(nevermassive-sdk,massive-api)
Prompt: (start Claude Code without providing an API key during plugin setup)
- Claude can still answer API knowledge questions from
.claude/CLAUDE.md - MCP tool calls fail gracefully with a clear auth error
- Claude suggests checking
MASSIVE_API_KEYin.envor the dashboard
Prompt: Get me a snapshot of AAPL, Bitcoin, Euro/USD, and the S&P 500 in a single call
- Claude uses
list_universal_snapshots(ticker_any_of=["AAPL", "X:BTCUSD", "C:EURUSD", "I:SPX"]) - Does NOT make four separate API calls
Prompt: How do I get futures aggregates for the E-mini S&P 500?
- Claude uses the correct futures endpoint path (
/futures/vX/aggs/{ticker}) - Does NOT use the equity aggregates endpoint
- Mentions the correct ticker format (e.g.,
ESM6for a contract, orESfor the product)
Prompt: /massive:scaffold live-tape websocket python (tester on free Basic plan)
- Claude stops before scaffolding and asks whether the user has Starter or above
- If tester confirms "no, Basic": Claude either scaffolds with a clear plan-tier note in the README or suggests a
restproject instead - If tester confirms "yes, Starter+": Claude proceeds to scaffold normally
Prompt: /massive:options "iron condor" SPY python (tester on free Basic Options plan)
- Claude stops and asks about Options Starter access before scaffolding
- If Basic Options: Claude offers a degraded version (open-interest/volume/strike filtering, no Greek-based ranking)
- If Options Starter+: Claude proceeds with full Greeks-based strategy
Prompt: The MCP server is not starting, what do I check?
- Claude references the README Troubleshooting section
- Mentions
uv --version, Python 3.12+ requirement,claude --debugfor logs - Does NOT fabricate a workaround that bypasses uvx
Prompt: How do I verify my plugin setup is working?
- Claude points to the 3-step verify-setup checklist in README (plugin loaded, MCP running, API key working)
| Section | Pass/Fail | Notes |
|---|---|---|
| 1. Plugin loading | ||
| 2. MCP server startup | ||
| 3a. Ticker formats | ||
| 3b. Python SDK patterns | ||
| 3c. JS SDK patterns | ||
| 3d. Go SDK patterns | ||
| 3e. Kotlin SDK patterns | ||
| 3f. get_* vs list_* | ||
| 3g. Pagination | ||
| 3h. Plan tiers | ||
| 3i. Gotchas | ||
| 4a. Endpoint search | ||
| 4b. API call | ||
| 4c. SQL query | ||
| 5a. Scaffold Python REST | ||
| 5b. Scaffold JS REST | ||
| 5c. Scaffold Go REST | ||
| 5d. Scaffold Kotlin REST | ||
| 5e. Scaffold WebSocket (all) | ||
| 5f. Discover (multi-lang) | ||
| 5g. Debug (multi-lang) | ||
| 5h. Options (multi-lang) | ||
| 5i. Dashboard (all focus) | ||
| 7. Brand/terminology | ||
| 8a. No API key | ||
| 8b. Mixed-asset snapshot | ||
| 8c. Futures awareness | ||
| 8d. Plan-tier guardrails | ||
| 8e. README troubleshooting |