Schwab
STDIOMCP server implementing Schwab API functionality through Model Context Protocol
MCP server implementing Schwab API functionality through Model Context Protocol
This is a server that implements the Model Context Protocol (MCP) for the Schwab API using schwab-py and the MCP python-sdk.
--jesus-take-the-wheel)# Install with all dependencies uv add -e . # Install development dependencies uv add -e .[dev]
The first step is to authenticate with the Schwab API and generate a token:
# Authenticate and generate a token uv run schwab-mcp auth --client-id YOUR_CLIENT_ID --client-secret YOUR_CLIENT_SECRET --callback-url YOUR_CALLBACK_URL
You can set these credentials through environment variables to avoid typing them each time:
SCHWAB_CLIENT_IDSCHWAB_CLIENT_SECRETSCHWAB_CALLBACK_URL (defaults to https://127.0.0.1:8182)By default, the token is saved to ~/.local/share/schwab-mcp/token.yaml (platform-specific). You can specify a different path:
uv run schwab-mcp auth --token-path /path/to/token.yaml
Both yaml and json token formats are supported and will be inferred from the file extension.
After authentication, you can run the server:
# Run the server with default token path uv run schwab-mcp server \ --client-id YOUR_CLIENT_ID \ --client-secret YOUR_CLIENT_SECRET \ --callback-url YOUR_CALLBACK_URL \ --discord-token YOUR_DISCORD_BOT_TOKEN \ --discord-channel-id YOUR_APPROVAL_CHANNEL_ID \ --discord-approver DISCORD_USER_ID [--discord-approver ANOTHER_USER_ID ...] # Run with a custom token path uv run schwab-mcp server \ --token-path /path/to/token.json \ --client-id YOUR_CLIENT_ID \ --client-secret YOUR_CLIENT_SECRET \ --callback-url YOUR_CALLBACK_URL \ --discord-token YOUR_DISCORD_BOT_TOKEN \ --discord-channel-id YOUR_APPROVAL_CHANNEL_ID # Run with account modification tools auto-approved (no Discord prompt) uv run schwab-mcp server \ --jesus-take-the-wheel \ --client-id YOUR_CLIENT_ID \ --client-secret YOUR_CLIENT_SECRET \ --callback-url YOUR_CALLBACK_URL
Token age is validated - if older than 5 days, you will be prompted to re-authenticate.
Discord-related flags can also be provided via environment variables:
SCHWAB_MCP_DISCORD_TOKENSCHWAB_MCP_DISCORD_CHANNEL_IDSCHWAB_MCP_DISCORD_TIMEOUT (optional, defaults to 600 seconds)SCHWAB_MCP_DISCORD_APPROVERS (optional comma-separated list of Discord user IDs)Published container images are available at ghcr.io/jkoelker/schwab-mcp. The image runs schwab-mcp server by default and persists token data under /root/.local/share/schwab-mcp unless overridden.
podman run --rm --interactive \ --env SCHWAB_CLIENT_ID \ --env SCHWAB_CLIENT_SECRET \ --env SCHWAB_CALLBACK_URL \ --env SCHWAB_MCP_DISCORD_TOKEN \ --env SCHWAB_MCP_DISCORD_CHANNEL_ID \ --publish 8182:8182 \ --volume ~/.local/share/schwab-mcp:/schwab-mcp \ ghcr.io/jkoelker/schwab-mcp:latest \ server \ --token-path /schwab-mcp/token.yaml
The container entrypoint reads the same environment variables as the CLI, so you can override or add flags by appending them (for example, ... ghcr.io/jkoelker/schwab-mcp:latest --jesus-take-the-wheel). To explore other entry points, run docker run ghcr.io/jkoelker/schwab-mcp:latest --help.
SCHWAB_MCP_DISCORD_TOKEN.None so the default authorization link is disabled.bot scope. When the permissions matrix appears, grant the bot:
WARNING: Using the
--jesus-take-the-wheelflag enables tools that can modify your account state. Use with caution as this allows LLMs to cancel orders and potentially perform other actions that change account state.
The server exposes the following MCP tools:
Note: Tools 27-39 will pause for Discord approval before executing. Pass
--jesus-take-the-wheelto bypass the approval workflow entirely.
get_datetime - Get the current datetime in ISO formatget_market_hours - Get market hours for a specific marketget_movers - Get movers for a specific indexget_instruments - Search for instruments with a specific symbolget_account_numbers - Get mapping of account IDs to account hashesget_accounts - Get information for all linked Schwab accountsget_accounts_with_positions - Get accounts with position informationget_account - Get information for a specific accountget_account_with_positions - Get specific account with position informationget_user_preferences - Get user preferences for all accounts including nicknamesget_order - Get details for a specific orderget_orders - Get orders for a specific accountget_quotes - Get quotes for specified symbolsget_advanced_price_history - Get advanced price history for a specific symbolget_price_history_every_minute - Get price history with minute frequencyget_price_history_every_five_minutes - Get price history with five minute frequencyget_price_history_every_ten_minutes - Get price history with ten minute frequencyget_price_history_every_fifteen_minutes - Get price history with fifteen minute frequencyget_price_history_every_thirty_minutes - Get price history with thirty minute frequencyget_price_history_every_day - Get price history with daily frequencyget_price_history_every_week - Get price history with weekly frequencyget_option_chain - Get option chain for a specific symbolget_advanced_option_chain - Get advanced option chain for a specific symbolget_option_expiration_chain - Get option expiration information for a symbolget_transactions - Get transactions for a specific accountget_transaction - Get details for a specific transaction--jesus-take-the-wheel flag)cancel_order - Cancel a specific orderplace_equity_market_order - Place a market order for a stock or ETFplace_equity_limit_order - Place a limit order for a stock or ETFplace_equity_stop_order - Place a stop order for a stock or ETFplace_equity_stop_limit_order - Place a stop-limit order for a stock or ETFplace_equity_order - Unified function for placing any equity order typecreate_option_symbol - Create a properly formatted option symbol from componentsplace_option_market_order - Place a market order for an option contractplace_option_limit_order - Place a limit order for an option contractplace_option_order - Unified function for placing any option order typeplace_one_cancels_other_order - Create an OCO order pair where execution of one cancels the otherplace_first_triggers_second_order - Create a sequence where one order triggers another upon executionplace_bracket_order - Create a complete strategy with entry order and OCO exit ordersThe --jesus-take-the-wheel flag enables LLMs to perform actions that can modify your account state, including:
These actions have direct financial implications. Only use this flag in controlled environments and with a complete understanding of the risks involved. Consider testing with small positions or in a paper trading account if available.
# Type check uv run pyright # Format code uv run ruff format . # Lint uv run ruff check . # Run tests uv run pytest
This project is available under the MIT License.