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

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:

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:

  1. echo "$MOONTOWER_API_KEY" is a real mtw_... key in the same shell that launched Claude Code.
  2. .mcp.json uses "${MOONTOWER_API_KEY}" (Claude Code expands this). A raw $MOONTOWER_API_KEY or an unexpanded ${...} will not authenticate.
  3. claude mcp get moontower shows https://api.moontower.ai/mcp. On the API-key path it also shows an X-API-Key header, 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:

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.

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.