Model Context Protocol
Moontower MCP Server
Moontower exposes a Model Context Protocol (MCP) server at /mcp so Claude Code and other MCP clients can call the API as typed tools.
Server URL
https://api.moontower.ai/mcp
Use that URL only. moontower.ai/mcp and app.moontower.ai/mcp are the product site, not the API.
1. Add the server
Run this on its own. It registers the server. It does not sign you in, and it does not take a header.
claude mcp add --transport http moontower https://api.moontower.ai/mcp
2. Sign in with OAuth
OAuth is the primary sign-in. In Claude Code, open /mcp, select moontower, choose Authenticate, and allow the connection in the browser.
/mcp → moontower → Authenticate → Allow
The first connection gets a 401 with WWW-Authenticate: Bearer resource_metadata=.... The client discovers the authorization server from /.well-known/oauth-protected-resource, registers itself, and opens moontower.ai/oauth/authorize. Sign in, review the client name, and click Allow. Nothing is copied by hand.
Connected clients are listed (and can be disconnected) at moontower.ai/api-keys under Connected MCP clients. Disconnecting revokes the refresh token immediately; the current access token stays valid for at most one hour.
3. Alternative for scripts and headless clients
Pass the same REST API key in the X-API-Key header. The value starts with mtw_. Never commit a key to .mcp.json, shell history, or project docs. The export below uses the placeholder mtw_....
export MOONTOWER_API_KEY="mtw_..."
claude mcp add --transport http moontower https://api.moontower.ai/mcp \
--header "X-API-Key: $MOONTOWER_API_KEY"
The same API-key path in .mcp.json. Claude Code expands ${MOONTOWER_API_KEY}. A raw $MOONTOWER_API_KEY, or an unexpanded ${MOONTOWER_API_KEY}, will not authenticate.
{
"mcpServers": {
"moontower": {
"type": "http",
"url": "https://api.moontower.ai/mcp",
"headers": {
"X-API-Key": "${MOONTOWER_API_KEY}"
}
}
}
}
If both X-API-Key and Authorization: Bearer are sent, the API key wins.
4. Verify
After you allow the connection, claude mcp get moontower should show https://api.moontower.ai/mcp. On the API-key path it should also show an X-API-Key header. Claude Code prints that header as ${MOONTOWER_API_KEY} even when the variable is set.
claude mcp get moontower
claude mcp list
Troubleshooting
OAuth: browser opens but the client never finishes
- The consent page says Upgrade required — the signed-in account is on the Free plan. API/MCP access starts at Individual.
- You approved but the client shows
invalid_grant— the authorization code is single-use and expires after 5 minutes; run Authenticate again. - The client keeps re-prompting — a refresh token was revoked from Connected MCP clients or expired after 30 days idle. Re-authenticate once.
API key: Failed to connect — HTTP 401 / Invalid OAuth error response
Claude Code shows "Invalid OAuth error response" when the initialize request is rejected with a 401 and its OAuth discovery fallback then fails (for example, apiv2.moontower.ai unreachable from your network). With a correctly configured API key the 401 never happens, so fix the key first.
Auth responses from get_api_user / _lookup_auth:
- 401
{"detail":"Authentication required. …"}— noX-API-Keyand noAuthorization: Bearerheader - 401
{"detail":"Invalid API key format"}— header present, value does not start withmtw_ - 401
{"detail":"Invalid API key"}— unknown hash, or key already deactivated - 401
{"detail":"API key has expired"}— key found butexpires_atis in the past (then deactivated) - 401
{"detail":"Invalid or expired access token"}— Bearer token failed signature/audience/issuer/expiry checks - 403
{"detail":"User account is not active"}— key is valid; userstatusis notACTIVE
Every 401 carries WWW-Authenticate: Bearer resource_metadata="https://api.moontower.ai/.well-known/oauth-protected-resource", ApiKey, which is what lets MCP clients offer the OAuth flow. Seeing OAuth discovery requests after a 401 is expected, not a missing server.
Check:
echo "$MOONTOWER_API_KEY"is a realmtw_...key in the same shell that launched Claude Code..mcp.jsonuses"${MOONTOWER_API_KEY}"(Claude Code expands this). A raw$MOONTOWER_API_KEYor an unexpanded${...}will not authenticate.claude mcp get moontowershowshttps://api.moontower.ai/mcp. On the API-key path it also shows anX-API-Keyheader, printed as${MOONTOWER_API_KEY}even when the variable is set.
Direct probe (bypasses Claude Code):
curl -sS -D- -X POST https://api.moontower.ai/mcp \
-H "X-API-Key: $MOONTOWER_API_KEY" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"probe","version":"0"}}}'
A valid key returns an MCP initialize result. A missing/bad key returns one of the JSON bodies above with server: cloudflare and no cf-mitigated header — that means you reached the origin, so the problem is the credential, not Cloudflare.
Cloudflare Error 1010 (browser signature banned)
Error 1010 is Cloudflare Browser Integrity Check, not application auth. It blocks some TLS/header fingerprints. MCP is machine-to-machine; a 1010 on /mcp is a WAF false positive.
Use https://api.moontower.ai/mcp only. moontower.ai/mcp and app.moontower.ai/mcp are the product site (Vercel), not the API.
If you hit 1010, send us:
- the Ray ID from the Cloudflare page
claude --version- whether you are on a VPN / corporate proxy
- whether a plain
curlprobe (above) also 1010s, or only Claude Code
A curl / claude mcp list that returns one of the origin JSON details above has already passed Cloudflare. Browser-impersonation workarounds (curl_cffi, spoofed JA3) are not required for a clean Claude Code install talking to this endpoint.
If 1010 also hits /v1/* from python-requests / httpx, it is the same zone-wide check, not MCP-specific.
Available Tools
The MCP server exposes the public FastAPI endpoints as typed tools, in the order the server lists them. Public tags are Market Data, Options Data, Trade Ideas, and Reference Data.
get_pricesget_earnings_datesget_implied_volatilityget_realized_volatilityget_volatility_coneget_constant_maturity_implied_volatilityget_iv_rankget_realized_vs_implied_volatilityget_volatility_skewget_option_chainget_early_exercise_analysiscompute_pnl_attributionget_implied_yieldget_options_pair_hedgeget_hedge_ratiosget_cockpit_statisticsget_ticker_directoryget_trade_ideas
Example Claude Prompts
Use Moontower to get the latest price and 30-day IV data for SPY and QQQ.
Find high-liquidity trade ideas for SPY, QQQ, and IWM, excluding earnings in the next 4 weeks.
Compare realized volatility vs implied volatility for AAPL over the last week.
Show me the volatility cone for TSLA. Is 30-day IV rich or cheap vs its realized history?
Use Moontower early exercise analysis for a TSLA 250 put expiring 2026-08-21. Is early exercise optimal?
Compute options pair hedge ratios: short QQQ 900 call vs long SPY 1010 call, both Dec 2028.
What hedge ratio do I need to hedge $1M of QQQ with SPY using Moontower hedge ratios?
Implementation Notes
The MCP server is generated from the FastAPI app with fastapi-mcp, so tool schemas and descriptions stay aligned with the OpenAPI definitions. Only public endpoint tags are exposed: Market Data, Options Data, Trade Ideas, and Reference Data.