Schwab MCP

Schwab Model Context Protocol (MCP) サーバーは、SchwabアカウントをLLMベースのアプリケーション(Claude Desktopやその他のMCPクライアントなど)に接続し、マーケットデータの取得、口座情報の確認、および(オプションで)あなたの監督下での注文発行を可能にします。

ドキュメント

Schwab Model Context Protocol Server

The Schwab Model Context Protocol (MCP) Server connects your Schwab account to LLM-based applications (like Claude Desktop or other MCP clients), allowing them to retrieve market data, check account status, and (optionally) place orders under your supervision.

Features

  • Market Data: Real-time quotes, price history, option chains, and market movers.
  • Account Management: View balances, positions, and transactions.
  • Trading: comprehensive support for equities and options, including complex strategies (OCO, Bracket).
  • Safety First: Critical actions (like trading) are gated behind a Discord approval workflow by default.
  • LLM Integration: Designed specifically for Agentic AI workflows.

Quick Start

Prerequisites

Installation

For most users, installing via uv tool or pip is easiest:

# Using uv (recommended for isolation)
uv tool install 'schwab-mcp[ta] @ git+https://github.com/jkoelker/schwab-mcp.git'

# Using pip
pip install 'schwab-mcp[ta] @ git+https://github.com/jkoelker/schwab-mcp.git'

The ta extra installs pandas and pandas-ta-classic for the technical-analysis tools (SMA, RSI, ...). Without it those tools are skipped at startup with a warning; pass --no-technical-tools to run without them deliberately. Add the signal extra (schwab-mcp[ta,signal]) for Signal approvals.

Pass secrets through environment variables (or schwab-mcp save-credentials) rather than command-line flags, which are visible to other local users in ps and end up in shell history:

export SCHWAB_CLIENT_ID=YOUR_KEY
export SCHWAB_CLIENT_SECRET=YOUR_SECRET
export SCHWAB_MCP_DISCORD_TOKEN=BOT_TOKEN   # only for Discord approvals

Authentication

Before running the server, you must authenticate with Schwab to generate a token file.

# If installed via uv tool or pip (with SCHWAB_CLIENT_ID / SCHWAB_CLIENT_SECRET exported)
schwab-mcp auth --callback-url https://127.0.0.1:8182

# If running from source
uv run schwab-mcp auth --callback-url https://127.0.0.1:8182

This will open a browser window for you to log in to Schwab. Once complete, a token will be saved to ~/.local/share/schwab-mcp/token.yaml.

Running the Server

Start the MCP server to expose the tools to your MCP client.

# Credentials come from SCHWAB_CLIENT_ID / SCHWAB_CLIENT_SECRET (see above).

# Basic Read-Only Mode (Safest)
schwab-mcp server

# Streamable HTTP (for reverse proxies / remote MCP connectors)
schwab-mcp server --http --host 127.0.0.1 --port 8000

# With Trading Enabled (Requires Discord Approval; token from SCHWAB_MCP_DISCORD_TOKEN)
schwab-mcp server \
  --discord-channel-id CHANNEL_ID \
  --discord-approver YOUR_USER_ID

# Or approve over Signal instead (requires a local bbernhard/signal-cli-rest-api
# daemon running in MODE=json-rpc or json-rpc-native; other modes cannot
# stream replies and silently consume them)
schwab-mcp server \
  --signal-account +15555550100 \
  --signal-approver +15555550199

Default transport is stdio (Claude Desktop and most local MCP clients). Use --http for FastMCP streamable-http when fronting the server with a gateway or remote connector (MCP endpoint is /mcp on the bound host:port).

Note: For trading capabilities, you must set up an approval backend: either a Discord bot (see Discord Setup Guide) or Signal via a local signal-cli REST daemon running in MODE=json-rpc (or json-rpc-native). Configure either backend, not both. (The signal extra declares the websockets dependency explicitly; a default install already includes it transitively.)

Configuration

You can configure the server using CLI flags or Environment Variables.

FlagEnv VariableDescription
--client-idSCHWAB_CLIENT_IDRequired. Schwab App Key.
--client-secretSCHWAB_CLIENT_SECRETRequired. Schwab App Secret.
--callback-urlSCHWAB_CALLBACK_URLRedirect URL (default: https://127.0.0.1:8182).
--token-pathN/APath to save/load token (default: ~/.local/share/...).
--httpN/AUse streamable-http transport instead of stdio.
--hostMCP_HOSTBind address when using --http (default: 127.0.0.1).
--portMCP_PORTBind port when using --http (default: 8000).
--jesus-take-the-wheelN/ADANGER. Bypasses Discord approval for trades.
--no-technical-toolsN/ADisables technical analysis tools (SMA, RSI, etc.).
--jsonN/AReturns JSON instead of formatted text (useful for some agents). Null/empty fields are stripped to reduce token usage.

Container Usage

A Docker/Podman image is available at ghcr.io/jkoelker/schwab-mcp.

podman run --rm -it \
  --env SCHWAB_CLIENT_ID=... \
  --env SCHWAB_CLIENT_SECRET=... \
  -v ~/.local/share/schwab-mcp:/schwab-mcp \
  ghcr.io/jkoelker/schwab-mcp:latest server --token-path /schwab-mcp/token.yaml

Available Tools

The server provides a rich set of tools for LLMs.

📊 Market Data

ToolDescription
get_quotesReal-time quotes for symbols.
get_market_hoursMarket open/close times.
get_moversTop gainers/losers for an index.
get_option_chainStandard option chain data.
get_price_history_*Historical candles (minute, day, week).

💼 Account Info

ToolDescription
get_accountsList linked accounts (pass include_positions=True for holdings).
get_accountBalances for one account by hash (pass include_positions=True for holdings).
get_transactionsHistory of trades and transfers.
get_ordersStatus of open and filled orders.

💸 Trading (Requires Approval)

Placing an order is two steps: preview it, then place it by ID. Every preview_* tool builds the order and calls Schwab's preview API to return the projected order details plus a preview_id; place_previewed_order then submits that exact previewed order — no re-derivation from parameters, so the LLM can't hallucinate a different order than what was reviewed. Replacing an order uses the same safe flow: preview a complete single-leg equity, option, or equity trailing-stop description with preview_replacement_order, then call replace_previewed_order for the bound target order.

ToolDescription
preview_equity_orderPreview a stock/ETF buy or sell.
preview_option_orderPreview an option contract buy or sell.
preview_bracket_orderPreview an entry + take-profit + stop-loss order. Stop-loss exit type defaults to STOP; pass loss_type (STOP, STOP_LIMIT, or LIMIT) for a different exit, plus loss_limit_price when loss_type is STOP_LIMIT; the response's resolved_leg_types shows what was actually built.
preview_replacement_orderPreview a complete single-leg replacement for an existing order.
place_previewed_orderPlace the exact order returned by a preview_* call, by preview_id. Requires approval.
replace_previewed_orderReplace the exact order bound to a replacement preview. Requires approval.
cancel_orderCancel an open order.

(See full tool list in src/schwab_mcp/tools/)

Development

To contribute to this project:

# Clone and install dependencies
git clone https://github.com/jkoelker/schwab-mcp.git
cd schwab-mcp
uv sync

# Run tests
uv run pytest

# Format and Lint
uv run ruff format . && uv run ruff check .

Mutation testing

Run a local mutation campaign with make mutation (two workers). The weekly GitHub Actions workflow runs Sundays at 07:29 UTC and also supports manual runs. CI limits the mutation step to 20 minutes and saves reports for 14 days; surviving mutants are informational and do not fail the build by score.

License

MIT License.