> ## Documentation Index
> Fetch the complete documentation index at: https://docs.joyride.exchange/llms.txt
> Use this file to discover all available pages before exploring further.

# MCP Server Guide

> Connect Joyride to Claude, Cursor, Codex, and other AI agents through MCP.

## What it exposes

The MCP server gives AI agents access to the Joyride options exchange through **18 tools**, **3 resources**, and **3 prompts** — covering market data, trading, account management, and wallet creation.

An AI agent can look up options chains, get quotes, place and cancel orders, check balances, and read positions — all through natural language. You talk to the agent; the agent calls the tools.

**Current mode:** Paper trading only. No real money at risk.

The MCP server is **stdio-only** and login-only. It reuses the session created by `joyride login`; account creation happens in the web app at [joyride.exchange](https://joyride.exchange).

## Quick start

```bash theme={null}
# 1. Install the Joyride CLI (includes the MCP server)
npm install -g joyride-cli

# 2. Verify it installed
joyride --help

# 3. Run the setup wizard (creates a wallet)
joyride setup

# 4. Log in (authenticates with the exchange, stores a JWT)
joyride login

# 5. Configure your AI tool (pick one):
joyride mcp install --client claude    # Claude Code
joyride mcp install --client cursor    # Cursor
joyride mcp install --client codex     # Codex

# 6. Restart your AI tool and start trading
#    "List available SOL instruments"
#    "Get a quote for SOL_USDC-3MAR26-75-C"
#    "Place a limit sell for 1 contract at $5"
```

For the full guided flow, start with [Agent Quickstart](../quickstart).

## Prerequisites

* **Node.js** >= 20.0.0
* **The Joyride CLI** — `npm install -g joyride-cli` (this installs both the CLI and MCP server)
* **A Joyride wallet** — created via `joyride wallet create` or `joyride setup`
* **An active session** — run `joyride login` (stores a JWT at `~/.joyride/session.json`)

The MCP server reads the stored JWT from `~/.joyride/session.json` on startup. No keypair path or passphrase is needed in the MCP config. If the session is expired or missing, the server exits with: "Not authenticated. Run `joyride login` first."

## Setup by client

### Claude Code

The fastest way:

```bash theme={null}
joyride mcp install --client claude
```

This runs `claude mcp add` under the hood and configures everything automatically.

To verify, run `/mcp` inside Claude Code. You should see:

```
joyride · connected
```

**Manual setup** (if you prefer editing config directly):

Create `.mcp.json` in your project root:

```json theme={null}
{
  "mcpServers": {
    "joyride": {
      "command": "joyride",
      "args": ["mcp", "serve"],
      "env": {
        "JOYRIDE_HTTP_URL": "https://joyride.exchange"
      }
    }
  }
}
```

Then restart Claude Code.

### Cursor

The fastest way:

```bash theme={null}
joyride mcp install --client cursor
```

This writes the correct entry to `~/.cursor/mcp.json` automatically, preserving any existing MCP servers you have configured.

**Manual setup:**

Add to `~/.cursor/mcp.json` (create the file if it doesn't exist):

```json theme={null}
{
  "mcpServers": {
    "joyride": {
      "command": "joyride",
      "args": ["mcp", "serve"],
      "env": {
        "JOYRIDE_HTTP_URL": "https://joyride.exchange"
      }
    }
  }
}
```

### Codex

```bash theme={null}
joyride mcp install --client codex
```

This runs `codex mcp add` under the hood.

### VS Code Copilot

No auto-install yet. Add to `.vscode/mcp.json` in your project:

```json theme={null}
{
  "servers": {
    "joyride": {
      "command": "joyride",
      "args": ["mcp", "serve"],
      "env": {
        "JOYRIDE_HTTP_URL": "https://joyride.exchange"
      }
    }
  }
}
```

### Generic / custom agents

Launch the MCP server directly over stdio:

```bash theme={null}
JOYRIDE_HTTP_URL=https://joyride.exchange \
joyride mcp serve
```

It speaks JSON-RPC over stdin/stdout per the [MCP specification](https://modelcontextprotocol.io). Authentication is handled via the stored JWT at `~/.joyride/session.json`.

## Reconfiguring and uninstalling

### Update an existing configuration

If you need to reconfigure (e.g., after changing connection URLs):

```bash theme={null}
joyride mcp install --client claude --force
```

The `--force` flag removes the existing config and writes a fresh one.

### Remove MCP from a client

```bash theme={null}
joyride mcp uninstall --client claude    # Claude Code
joyride mcp uninstall --client cursor    # Cursor
joyride mcp uninstall --client codex     # Codex
```

Uninstall is safe to run even if joyride is not currently configured — it's a no-op.

For Cursor, uninstall removes only the `joyride` entry from `~/.cursor/mcp.json` and preserves all other MCP servers.

### Uninstall the CLI entirely

```bash theme={null}
npm uninstall -g joyride-cli
```

This removes both the CLI and the bundled MCP server.

## Client support

| Client          | Auto-install                          | Status                   |
| --------------- | ------------------------------------- | ------------------------ |
| Claude Code     | `joyride mcp install --client claude` | Supported and tested     |
| Cursor          | `joyride mcp install --client cursor` | Supported and tested     |
| Codex           | `joyride mcp install --client codex`  | Supported and tested     |
| VS Code Copilot | Manual config (see above)             | Configuration documented |

<Note>
  The MCP server runs over **stdio only**. There is no hosted HTTP MCP endpoint — the `:3002` hosted server and the `/api/v1/mcp` route were removed at the core cutover. Install it locally with `joyride mcp install` (or run the bundled `joyride-mcp` binary / `joyride mcp serve`). Hosted, multi-user MCP will return later as a platform feature with real per-user auth.
</Note>

## Auto-approve permissions

By default, Claude Code asks permission before calling each MCP tool. To skip prompts for Joyride tools, add them to `.claude/settings.json`:

```json theme={null}
{
  "permissions": {
    "allow": [
      "mcp__joyride__list_instruments",
      "mcp__joyride__get_quote",
      "mcp__joyride__get_orderbook",
      "mcp__joyride__get_options_chain",
      "mcp__joyride__get_ticker",
      "mcp__joyride__get_all_tickers",
      "mcp__joyride__get_market_config",
      "mcp__joyride__get_index_price",
      "mcp__joyride__place_order",
      "mcp__joyride__cancel_order",
      "mcp__joyride__cancel_all_orders",
      "mcp__joyride__get_open_orders",
      "mcp__joyride__get_order_status",
      "mcp__joyride__get_balance",
      "mcp__joyride__get_account",
      "mcp__joyride__get_positions",
      "mcp__joyride__get_trade_history",
      "mcp__joyride__create_wallet"
    ]
  }
}
```

**Tip for live trading:** Remove `place_order`, `cancel_order`, and `cancel_all_orders` from the allow list so those still require manual confirmation.

## Tool reference

All tools accept and return human-friendly units. Machine-readable prices and sizes are decimal strings so values remain exact:

* **Prices** in USD (e.g., `"5.00000000"`)
* **Sizes** in contracts (e.g., `"1.00000000"`, `"0.50000000"`)
* **Sides** as `"buy"` / `"sell"`

### Market data (7 tools)

#### `list_instruments`

List available options contracts with optional filters.

| Parameter | Type                | Required | Description                                 |
| --------- | ------------------- | -------- | ------------------------------------------- |
| `asset`   | string              | No       | Filter by asset (`"SOL"`, `"BTC"`, `"ETH"`) |
| `expiry`  | string              | No       | Filter by expiry date (`"3MAR26"`)          |
| `type`    | `"call"` \| `"put"` | No       | Filter by option type                       |

**Example prompt:** "List all SOL call options"

**Transport:** HTTP (public API)

#### `get_quote`

Get real-time bid/ask/mid quote for a specific instrument.

| Parameter       | Type   | Required | Description                  |
| --------------- | ------ | -------- | ---------------------------- |
| `instrument_id` | string | Yes      | e.g., `SOL_USDC-3MAR26-75-C` |

**Returns:** `{ instrument, bid, ask, mid, last, bookSeq, synced }`. Price fields are exact decimal strings in USD, or `null` for empty sides.

**Transport:** WebSocket (MD)

#### `get_orderbook`

Get full order book depth for a specific instrument.

| Parameter       | Type   | Required | Description                         |
| --------------- | ------ | -------- | ----------------------------------- |
| `instrument_id` | string | Yes      | Instrument ID                       |
| `depth`         | number | No       | Price levels per side (default: 10) |

**Returns:** `{ instrument, bookSeq, bids: [{price, size}], asks: [{price, size}] }`. Price and size fields are exact decimal strings.

**Transport:** WebSocket (MD)

#### `get_options_chain`

Get all options for an asset organized by strike (calls and puts side by side).

| Parameter | Type   | Required | Description               |
| --------- | ------ | -------- | ------------------------- |
| `asset`   | string | Yes      | `"SOL"`, `"BTC"`, `"ETH"` |
| `expiry`  | string | No       | Filter by expiry date     |

**Returns:** `{ asset, expiry, strikes: [{ strike, call?, put? }] }`; each leg includes its instrument metadata and a synthesized quote when available. Fixed-point values are decimal strings.

**Transport:** WebSocket + HTTP

#### `get_ticker`

Get ticker snapshot for a specific instrument.

| Parameter       | Type   | Required | Description   |
| --------------- | ------ | -------- | ------------- |
| `instrument_id` | string | Yes      | Instrument ID |

**Returns:** `{ instrument, bestBid, bestBidSize, bestAsk, bestAskSize, mid, last, spread, synced }` — exact decimal strings for non-null fixed-point values. There is no mark, IV, or 24h stats surface in alpha.

**Transport:** WebSocket (MD)

#### `get_all_tickers`

Get all reduced tickers in one call.

**Returns:** Array of the same reduced ticker shape as `get_ticker`. A symbol whose book cannot sync is returned with null values and `synced: false`.

**Transport:** WebSocket (MD)

#### `get_market_config`

Get market protocol configuration.

**Returns:** `{ version, priceDecimals, quantityDecimals }`

**Transport:** HTTP

### Trading (5 tools)

All trading tools require wallet authentication.

#### `place_order`

Place a limit or market order.

| Parameter       | Type                                           | Required  | Description                                |
| --------------- | ---------------------------------------------- | --------- | ------------------------------------------ |
| `instrument_id` | string                                         | Yes       | Instrument ID                              |
| `side`          | `"buy"` \| `"sell"`                            | Yes       | Order side                                 |
| `type`          | `"limit"` \| `"market"`                        | No        | Default: `"limit"`                         |
| `price`         | decimal string                                 | For limit | Limit price in USD (e.g., `"5.00"`)        |
| `size`          | decimal string                                 | Yes       | Number of contracts (e.g., `"1"`, `"0.5"`) |
| `time_in_force` | `"gtc"` \| `"ioc"` \| `"fok"` \| `"post_only"` | No        | Optional time-in-force override            |

**Returns:** an outcome with `status: "acknowledged"`, `"open"`, or `"filled"`. Acknowledged orders include the core order report; timeout reconciliation includes the matching open order or fill. An unresolved write returns an `ACK_TIMEOUT_UNKNOWN` MCP error and is never silently resent.

**Example prompts:**

* "Sell 1 contract of SOL\_USDC-3MAR26-75-C at \$5"
* "Buy 2 contracts of SOL\_USDC-3MAR26-75-P at market"

**Transport:** WebSocket

#### `cancel_order`

Cancel a specific order.

| Parameter       | Type   | Required | Description        |
| --------------- | ------ | -------- | ------------------ |
| `instrument_id` | string | Yes      | Instrument ID      |
| `order_id`      | number | Yes      | Order ID to cancel |

**Transport:** WebSocket

#### `cancel_all_orders`

Cancel all open orders, optionally for a specific instrument.

| Parameter       | Type   | Required | Description                            |
| --------------- | ------ | -------- | -------------------------------------- |
| `instrument_id` | string | No       | Cancel only orders for this instrument |

**Returns:** `{ requested, cancelled, failed, complete, results }`. Matching orders are drained in server-filtered concurrent batches, and `complete: true` is returned only after an empty read. Any per-order failure or `complete: false` is returned as an MCP error, because the panic button cannot claim success while orders may remain.

**Transport:** HTTP (query API, Bearer) + WebSocket

#### `get_open_orders`

List the active/open orders currently visible in query-api. Query-api has no cursor.

**Returns:** `{ orders: [{ orderId, symbol, side, price, quantity, filledQuantity, status, createdAt }], complete }`. Until query-api exposes pagination metadata, a non-empty response conservatively reports `complete: false`.

**Transport:** HTTP (query API, Bearer)

#### `get_order_status`

Check the status of a specific order with query-api's exact, account-scoped `order_id` filter. Terminal-state order history is not available in alpha, so an order id that is not currently open returns a deterministic notice (it may have filled, been cancelled, or expired) rather than a bare "not found".

| Parameter  | Type   | Required | Description         |
| ---------- | ------ | -------- | ------------------- |
| `order_id` | number | Yes      | Order ID to look up |

**Transport:** HTTP (query API, Bearer)

### Account (4 tools)

All account tools require wallet authentication.

#### `get_balance`

Get current available collateral. Core does not expose a reserved/locked breakdown.

**Returns:** `{ accountId, available, updatedAt }`; `available` is an exact decimal string. An unfunded account returns `{ available: null, note }`.

**Transport:** HTTP (query API, Bearer)

#### `get_account`

Get full account summary (balance and positions).

**Returns:** `{ accountId, walletPubkey, feeTier, available, positions: [{symbol, quantity, avgPrice, updatedAt}] }`

**Transport:** HTTP (query API, Bearer)

#### `get_positions`

Get all open positions.

**Returns:** Array of `{ symbol, quantity, avgPrice, updatedAt }` — raw positions only. Mark, PnL, current value, and breakeven enrichment is not available in alpha.

**Transport:** HTTP (query API, Bearer)

#### `get_trade_history`

Get recent trade fills/executions.

| Parameter | Type   | Required | Description                        |
| --------- | ------ | -------- | ---------------------------------- |
| `limit`   | number | No       | Maximum number of trades to return |

**Transport:** HTTP (query API, Bearer)

### Wallet (1 tool)

#### `create_wallet`

Create a new Solana wallet for trading. The wallet is encrypted and stored locally. Returns the wallet address.

**Transport:** local

### Spot (1 tool)

#### `get_index_price`

Get the underlying spot/index price for an asset, from the oracle feed.

| Parameter | Type   | Required | Description               |
| --------- | ------ | -------- | ------------------------- |
| `asset`   | string | Yes      | `"SOL"`, `"BTC"`, `"ETH"` |

**Returns:** `{ asset, price, timestamp }`

**Transport:** WebSocket (oracle)

<Note>
  **Removed at core cutover.** `get_greeks`, `get_price_history`, `get_positions_with_metrics`, `get_order_history`, and `get_profiles` are no longer part of the tool set — the core exchange exposes no public Greeks, candle, position-metrics, terminal-order-history, or social surface. An absent tool is better agent UX than one that always errors.
</Note>

## Resources

MCP resources provide read-only data snapshots that clients can request.

| URI                   | Description                                                                  |
| --------------------- | ---------------------------------------------------------------------------- |
| `joyride://portfolio` | Account id, wallet, available balance, and raw positions (no PnL enrichment) |
| `joyride://markets`   | Open-instrument catalog; use quote/ticker tools for live prices              |
| `joyride://config`    | Authentication status, paper-trading mode, and server version                |

Resources return JSON. They're useful for MCP clients that support resource browsing.

## Prompts

MCP prompts are pre-built multi-step workflows that guide the AI agent through structured analysis.

### `analyze-position`

Analyze a specific options position from raw position data, a synthesized quote, and spot. (Greeks, mark, and IV are not available in alpha, so the analysis is reduced accordingly.)

| Argument        | Type   | Description              |
| --------------- | ------ | ------------------------ |
| `instrument_id` | string | Instrument ID to analyze |

### `screen-opportunities`

Screen observable 0DTE candidates using quotes, liquidity, spot distance, breakeven, and max loss. The prompt does not claim to identify a single best trade without IV, greeks, or probability inputs.

| Argument | Type   | Description                                  |
| -------- | ------ | -------------------------------------------- |
| `asset`  | string | Asset to analyze (`"SOL"`, `"BTC"`, `"ETH"`) |

### `build-strategy`

Construct an options strategy based on market outlook (analysis only — each leg must be executed as a separate `place_order` call).

| Argument  | Type   | Description                                         |
| --------- | ------ | --------------------------------------------------- |
| `asset`   | string | Asset to build strategy for                         |
| `outlook` | string | `"bullish"`, `"bearish"`, `"neutral"`, `"volatile"` |

## Instrument ID format

All instrument IDs follow this pattern:

```
{ASSET}_USDC-{DMMMYY}-{STRIKE}-{C|P}
```

| Component | Description                     | Examples              |
| --------- | ------------------------------- | --------------------- |
| `ASSET`   | Underlying asset                | `SOL`, `BTC`, `ETH`   |
| `DMMMYY`  | Expiry date (1- or 2-digit day) | `3MAR26`, `27FEB26`   |
| `STRIKE`  | Strike price (integer USD)      | `75`, `100`, `200`    |
| `C\|P`    | Option type                     | `C` (call), `P` (put) |

Use `list_instruments` to discover valid IDs. Instruments are 0DTE (same-day expiry) and refresh daily.

## Configuration

The MCP server loads configuration in this order (highest priority first):

1. **Environment variables** (set in your MCP client config or shell)
2. **Config file** (`~/.joyride/config.toml`)
3. **Defaults**

A missing config file uses defaults. A malformed config file fails startup instead of silently falling back to a different endpoint or wallet.

### Environment variables

| Variable                 | Default                    | Description                                                                                           |
| ------------------------ | -------------------------- | ----------------------------------------------------------------------------------------------------- |
| `JOYRIDE_HTTP_URL`       | `https://joyride.exchange` | Single public origin. The trading WS, MD WS, oracle WS, and query/public API URLs all derive from it. |
| `JOYRIDE_TRADING_WS_URL` | (derived)                  | Override the core trading WS URL explicitly.                                                          |
| `JOYRIDE_MD_WS_URL`      | (derived)                  | Override the core market-data WS URL explicitly.                                                      |

Authentication is handled via the stored JWT at `~/.joyride/session.json` (created by `joyride login`). No credential-related env vars are needed in the MCP config.

### Config file

The MCP server shares `~/.joyride/config.toml` (config schema v2) with the CLI. Run `joyride setup` to create it interactively — the wizard takes a single origin and derives every service URL from it:

```toml theme={null}
[auth]
wallet_address = "GmQozSzrtMjXt5F1Bed8Vrt55zCbiga8vDZr47RX9wC8"

[defaults]
asset = "SOL"
mode = "paper"

[connection]
http_url = "https://joyride.exchange"
```

## Example workflows

### Discover and quote

```
You: What SOL options are available today?
Agent: [calls list_instruments] -> 72 instruments

You: Get a quote on the $75 call
Agent: [calls get_quote] -> bid: $3.50, ask: $5.00, mid: $4.25

You: Show me the full options chain
Agent: [calls get_options_chain] -> organized by strike with calls/puts side by side
```

### Place and manage orders

```
You: Sell 1 contract of the $75 call at $5
Agent: [calls place_order] -> Order #1994754 placed, status: open

You: Show my open orders
Agent: [calls get_open_orders] -> 1 open order

You: Cancel that order
Agent: [calls cancel_order] -> Order cancelled

You: Check my balance
Agent: [calls get_balance] -> Available: $10,000.00
```

### Guided analysis (using prompts)

```
You: Screen the available SOL opportunities right now
Agent: [uses screen-opportunities prompt] -> compares quoted candidates and flags liquidity limits

You: Build a bullish strategy for SOL
Agent: [uses build-strategy prompt] -> suggests strategies with specific strikes and sizing
```

## Troubleshooting

### MCP server doesn't appear in `/mcp`

Run `joyride mcp install --client claude` to configure automatically. If you set it up manually, the config must be in `.mcp.json` at the project root. Restart Claude Code after creating or editing it.

### Server shows failed

Check the server logs:

```bash theme={null}
joyride mcp serve 2>&1 | head -20
```

Common causes:

* No valid session — run `joyride login` first
* Node.js version too old — requires >= 20.0.0

### `Not authenticated. Run joyride login first.`

The MCP server could not find a valid JWT at `~/.joyride/session.json`. Run `joyride login` in your terminal to authenticate, then restart your MCP client.

### `NOT_AUTHENTICATED` or session expired errors

Your JWT has expired. Run `joyride login` again to get a fresh session, then restart your MCP client.

### Connection warning

If you see a connection error on startup, the core stack is unreachable. The server starts in degraded mode — tools requiring the WebSocket will return errors, but HTTP query tools still work.

Check that `JOYRIDE_HTTP_URL` is set to a reachable origin (default `https://joyride.exchange`).

### Permission prompts on every tool call

See [auto-approve permissions](#auto-approve-permissions) to pre-approve all Joyride tools.

### Quotes show all nulls

The order book is empty. Use `list_instruments` to find instruments with activity.

### Debug mode

```bash theme={null}
JOYRIDE_HTTP_URL=https://joyride.exchange \
joyride mcp serve 2>mcp-debug.log
```

Check `mcp-debug.log` for auth status, connection warnings, and errors.
